Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize #30

Merged
merged 1 commit into from
Oct 23, 2021
Merged

Optimize #30

merged 1 commit into from
Oct 23, 2021

Conversation

minad
Copy link
Contributor

@minad minad commented Oct 22, 2021

Extract separate function to allow compilation instead of slow evaluation. Use
cl-loop instead of seq-filter. This optimization helps when scrolling through
large documents, where previously the minions mode line eval appeared in the
profile for redisplay.

(You may also want to consider tagging a version?)

@tarsius
Copy link
Owner

tarsius commented Oct 22, 2021

I think the problem is that you did not compile minions.el. And uncompiled the problem isn't seq-filter but pcase-lambda. cl-loop is actually slower than the alternatives.

(defun minions--benchmark ()
  (let ((minions-direct '(smerge-mode symbol-overlay-mode diff-minor-mode))
	(fn (byte-compile (pcase-lambda (`(,mode))
			    (memq mode minions-direct)))))
    (list
     (cons "cl-loop                                 "
           (benchmark-run 1000 (cl-loop for mode in minor-mode-alist
				        if (memq (car mode) minions-direct)
				        collect mode)))
     (cons "cl-remove-if-not lambda                 "
           (benchmark-run 1000 (cl-remove-if-not (lambda (elt)
					           (memq (car elt) minions-direct))
					         minor-mode-alist)))
     (cons "seq-filter       lambda                 "
           (benchmark-run 1000 (seq-filter (lambda (elt)
				             (memq (car elt) minions-direct))
                                           minor-mode-alist)))
     (cons "cl-remove-if-not pcase-lambda           "
           (benchmark-run 1000 (cl-remove-if-not (pcase-lambda (`(,mode))
					           (memq mode minions-direct))
					         minor-mode-alist)))
     (cons "seq-filter       pcase-lambda           "
           (benchmark-run 1000 (seq-filter (pcase-lambda (`(,mode))
				             (memq mode minions-direct))
                                           minor-mode-alist)))
     (cons "cl-remove-if-not pcase-lambda (compiled)"
           (benchmark-run 1000 (cl-remove-if-not fn minor-mode-alist)))
     (cons "seq-filter       pcase-lambda (compiled)"
           (benchmark-run 1000 (seq-filter fn minor-mode-alist))))))

Uncompiled:

(("cl-loop                                 " 0.056413429 0 0.0)
 ("cl-remove-if-not lambda                 " 0.045764128 0 0.0)
 ("seq-filter       lambda                 " 0.045056775 0 0.0)
 ("cl-remove-if-not pcase-lambda           " 0.154783135 0 0.0)
 ("seq-filter       pcase-lambda           " 0.149898836 0 0.0)
 ("cl-remove-if-not pcase-lambda (compiled)" 0.023883365 0 0.0)
 ("seq-filter       pcase-lambda (compiled)" 0.023564509 0 0.0))

Compiled:

(("cl-loop                                 " 0.021798277 0 0.0)
 ("cl-remove-if-not lambda                 " 0.018225872 0 0.0)
 ("seq-filter       lambda                 " 0.017266483 0 0.0)
 ("cl-remove-if-not pcase-lambda           " 0.018810671 0 0.0)
 ("seq-filter       pcase-lambda           " 0.01863012 0 0.0)
 ("cl-remove-if-not pcase-lambda (compiled)" 0.010604115 0 0.0)
 ("seq-filter       pcase-lambda (compiled)" 0.013582858 0 0.0))

Not sure why it makes whether the lambda is explicitly compiled, when everything is compiled. Could be interesting to look in, I am guessing that it's not a closure when compiled explicitly.

(You may also want to consider tagging a version?)

Not sure what happened to the tags. I have pushed them again.

I don't add a Version header keyword unless I have to (when publishing to [Non]GNU Elpa).

@minad
Copy link
Contributor Author

minad commented Oct 22, 2021

I think the problem is that you did not compile minions.el. And uncompiled the problem isn't seq-filter but pcase-lambda. cl-loop is actually slower than the alternatives.

No, the problem is that the :eval expression is not compiled since it is a list literal. Of course I use a compiled minions package. But you are right that cl-loop is not the best choice here, but it matters much less than using compiled vs non-compiled evaluation for the :eval expression.

@minad
Copy link
Contributor Author

minad commented Oct 22, 2021

I pushed an improved version which uses cl-remove-if-not.

I don't add a Version header keyword unless I have to (when publishing to [Non]GNU Elpa).

What about MELPA Stable?

minions.el Show resolved Hide resolved
Extract a separate function to allow compilation instead of slow
evaluation.  This optimization helps when scrolling through large
documents, where previously the minions mode line eval appeared
in the profile for redisplay.

The bottleneck is `pcase-lambda', which performs horribly when not
compiled.  Because the whole file might not be compiled drop using
it altogether.  Also use `cl-filter-if-not' instead of `seq-filter',
for an additional, though tiny speedup, and also because it allows
dropping a dependency.
@tarsius
Copy link
Owner

tarsius commented Oct 22, 2021

Ah sorry, I didn't read your description properly and immediately bounced on the switch to cl-loop.

I improved the commit message a bit. Does this look okay to you?

@tarsius
Copy link
Owner

tarsius commented Oct 22, 2021

What about MELPA Stable?

It gets the version from the latest version tag, not from the Version keyword.

@minad
Copy link
Contributor Author

minad commented Oct 23, 2021

Yes, the commit message is better. Thanks!

@tarsius tarsius merged commit 3cc45f8 into tarsius:master Oct 23, 2021
@tarsius
Copy link
Owner

tarsius commented Oct 23, 2021

Thanks to you too!

@tarsius
Copy link
Owner

tarsius commented Oct 23, 2021

By the way, how did you get your hands on "the profile for redisplay"?

@minad
Copy link
Contributor Author

minad commented Oct 23, 2021

So I thought this should not be postponed any longer.

I profiled a new package of mine which was quite slow during redisplay when scrolling around due to too many text properties. I was able to optimize the scroll redisplay a bit by reducing and sharing more of the attached properties. This minions issue was just an incidental finding. There was some high cost related to the mode line, eval and seq-filter. I am using a very simple mode line - basically only minions, so the issue was quickly found.

Btw, thanks a lot for minions! I really like that I don't have to configure anything besides a few minions-direct modes to get a less distractive modeline.

@tarsius
Copy link
Owner

tarsius commented Oct 27, 2021

This minions issue was just an incidental finding.

I was more wondering about how than why, as it would probably be a good idea to perform some redisplay benchmarking but I don't know how.

Btw, thanks a lot for minions!

You are welcome! I like your packages too. 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants