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
Performance issues on OSX #2909
Comments
More profiling results, this time from a new completely empty repo.
Half a second+ seems excessive for this case, no? |
The cause of this probably is System Integrity Protection a new of OS X El Capitan. You should disable that as described here (you might better instructions elsewhere). |
Not sure how SIP plays into this, can you elaborate? Why would SIP affect git being executed through I use my own git executable which is placed in /opt/local, nothing to do with Xcode. I instrumented some of the frequently called functions to print their arguments.
A lot/most of these calls look completely redundant to me. |
No, I don't use macOS and I don't know any more about it. It's just that other users had the same issue before and disabling SIP made it go away, so there is a good change that might work for you too.
If calling git is slow, that might never-the-less be unnoticeable but when calling it multiple times as is necessary to refresh magit's status buffer, the slowdown accumulates.
Make sure this is really the case. Emacs and your shell might not use the same $PATH.
How exactly did you do that? Show my the advice or patch.
It is called 27 times to do that and to update the status buffer.
Actually it doesn't. Magit uses a cache now and most of these identical calls to git are actually reduced to just one. Git is still being called more than once, so the effect of whatever makes calling git slow still accumulates. (That's why I am asking for the patch/advice: to see whether it takes the cache into account.)
In many places we need to know where the repository is, so we have to figure it out. But we use a cache because starting subprocesses is slow on Windows. If you get unlucky, it can also be slow on macOS, probably because of the issue I mentioned. Please give disabling System Integrity Protection a change. Maybe there is another issue, but I would first like to rule this likely cause out. |
Calling git once (to stage) through magit-process-file takes about the same amount of time to calling "git
From my previous post here:
You can clearly see the path is correct.
I added (message "process-file: %s" args) in the beginning of magit-process-file.
Through printing the arguments of magit-process-file we can see that process-file ends up getting called 27 times for 1 stage operation, each time creating a new git process. Now, AFAIK, process-file does not use a cache, so if you say that Magit is using a cache now that should be in-effect before you end up calling Emacs process-file. Since we can see the latter get called 27 times, that doesn't seem to be the case here. Maybe it's a configuration issue or maybe something else is causing the Magit cache to be bypassed. I will disable SIP and get back to you on that too. |
I am also experiencing this problem (OSX El Capitan, with SIP/rootless already disabled, homebrew git@2.11.0, This is the top of a profile from
I do have some customizations to magit hooks, so if necessary I could rerun this from a clean config if it'd be helpful. |
jguenther: In your case, magit-process-file ended up being called 77 (!) times. Surely there's something wrong here. EDIT: I tried again on my setup with SIP disabled, no difference at all. |
77 operations for a stage and then unstage, but yeah that does seem excessive. I will try to do this from an empty emacs config with just magit installed and see if it's any different. |
But it's a somewhat recent development? The cache I mentioned earlier only covers "refreshing the status buffer, after something made that necessary". It does not cover "save unsaved file-visiting buffers before refresh" (I think, will have to check. Also is might be fairly simple to change that.) And it also doesn't cover "revert file-visiting buffers after files have changed on disc" (using a cache here would be rather difficult). |
Let's say there's room for improvement here... I'll look into this in more depth as soon as possible, probably not before the second half of January though. (Unless I find a quick solution during the next few hours.) |
I could swear it wasn't this slow in the past, otherwise I would have noticed. |
Yes and no. First, this is the result of very old design decisions (in short "just regenerate everything, when something might have changed") and I plan to address that soon. However that will be a major amount of work, as that will involve completely refactoring many central parts of magit. Looking at the arguments a bit closer I see that diff --git a/lisp/magit-git.el b/lisp/magit-git.el
index db830b93..d5130f79 100644
--- a/lisp/magit-git.el
+++ b/lisp/magit-git.el
@@ -1496,7 +1496,7 @@ (defun magit-config-get-from-cached-list (key)
(--> key
(replace-regexp-in-string "\\`[^.]+" #'downcase it t t)
(replace-regexp-in-string "[^.]+\\'" #'downcase it t t))
- (magit--with-refresh-cache (list 'config (magit-toplevel))
+ (magit--with-refresh-cache (list 'config default-directory)
(let ((configs (make-hash-table :test 'equal)))
(dolist (conf (magit-git-items "config" "--list" "-z"))
(let* ((nl-pos (cl-position ?\n conf)) @npostavs Do you think this is safe (i.e. never slower than no caching at all)? Not doing it kinda defeats the purpose of using As for the other calls
there isn't much that can be done about those without major refactoring. There are multiple strategies for improving performance that I intend to implement, all essentially boiling down to "don't do things that aren't actually necessary". Or in other words make Magit smarter, by investing the time necessary to make that happen (lot's of work). I actually already intended to write about that fairly soon. I am afraid I don't see a way to improve this significantly any time soon, i.e. before the rewrite. Except for the thing mentioned above. Also if you can find causes for the slowness that are more concrete than "git is being called to often" (I've known that for a long time), then I might be able to squeeze out a bit of performance here and there. But overall the time for squeezing is over, what's needed now are some major changes. |
@tarsius Ok, I suspected as much, it's sorta obvious from the profiling results. I'll still do the bisect and see if there's a specific point where things got noticeably worse. |
Those calls are not from Most of the calls seem to be from
I used |
Ah, I think we can optimize the process part. We already store the |
I am closing this because there's not anything I can do right now, it's a known issue, and I am going to address it eventually anyway. But if you have concrete suggestions for tweaks, then keep them coming. |
If it's a known issue can't you leave this open (or rename it to be more generic since it applies to other OSes too) so we can track it? If you're dead-set on closing this, can you at least update the Performance section of the manual to make it obvious that "calling git too much" can be an issue. Right now the manual mentions refreshing buffers and recreating their content from scratch, which could be a different issue entirely, or it could be the root cause of calling git too much or both as implied by the profiling data (refreshing adds its own overhead on top of the time spent by calling out to git). If you make it obvious that calling out to git a lot is a problem, others will have more information to get going with, assuming they want to dive in and attempt to fix. |
Okay we can keep this open.
As I said that requires major refactoring, so they won't succeed (unless they spend a few months on it, which I will do). |
Also I closed this with the intention to very soon write about this in more detail. Once I have done that, I will close this issue again. |
Will throw my hat in the ring and also say that Linux's Emacs / Magit seem to be way faster than on OSX. Ubuntu inside a VM on my Windows 10 desktop has instantaneous everything whereas every action on OSX has probably a 500-700 ms delay to it. It's just surprising staging a file doing OSX -> Remote Into Windows -> Ubuntu in Vmware is faster than native OSX staging a file. Though my desktop is probably much faster than my macbook 6850K processor vs whatever is in the 2013 Macbook. It's on my TODO list at some point to test a Linux VM from the Macbook and see if it really is just a hardware thing and not something else intrinsic to OSX causing this slowness. |
I have tried disabling SIP and I confirm it does not fix the issue. If someone has a workaround for this it would be greatly appreciated. I have also tried a blank emacs config with only magit installed and the issue is still there. (Other places mentioned that performance issues my be related to themes or packages.) My initial awareness of this issues comes from working on the same repo from a 2013 MBP running linux (magit is blazing fast) and from a 2017 MBP running Sierra (annoying lag on just about every magit operation). |
I have installed VirtualBox and setup a Fedora 25 VM to test @jojojames's hypothesis. Although, using that VM (i.e. dragging windows around, typing in terminal) is slow and laggy, magit is snappy and much faster than on the macOS sierra host. |
Is it git itself that is slower? Can you time some git commands on the VM and compare to macOS? |
Benchmark of Aquamacs (http://aquamacs.org/):
|
For me on macOS 10.11 w/
Strangely, with my full spacemacs config, it doubles:
These are both results of running:
What could cause that doubling? EDIT It has to do with the frame size. The larger the frame, the longer |
FYI, reported upstream: http://lists.gnu.org/archive/html/bug-gnu-emacs/2017-04/msg00201.html and YAMAMOTO Mitsuharu has an idea. |
Thanks @aaronjensen! |
I can confirm that doing what was suggested makes for a significant improvement. I now understand why people consider magit more than fast enough (who do not use macs). Very exciting 😄 I've submitted a pull to add an experimental patch to homebrew-emacs-plus. If anyone else wants to try it before it is merged, know that it is experimental, it could cause emacs to hang or who knows what else. That said, if you want to: $ brew tap d12frosted/emacs-plus
$ brew edit emacs-plus And add (somewhere with the other patches): patch do
url "https://gist.githubusercontent.com/aaronjensen/28e26a6c6c0dc767176bc35d5545d10e/raw/42b53ce8cca354f9a6249ffa63b5773abcef958e/GNU-Emacs-25.1-OS-X-enable-vfork.patch"
sha256 "37d699b42754994c8aa86f245f418b0e26cf7b4b48f29de58117086663e73ac2"
end Then: $ brew reinstall emacs-plus |
This is great. Thanks @jsab @npostavs @aaronjensen @tarsius I wonder what benchmarks we are getting. I'm only getting 2-3 ms difference. Yamamoto Port Built off Master with
(benchmark 1 '(call-process "/usr/bin/true" nil nil nil)) |
Hallelujah! The @aaronjensen patch works for me:
(That is with my full spacemacs config, running Note that for people who originally did not install emacs-plus from source you will need to build from source for the patch to take effect after you added the snippet with brew edit:
In conclusion, this makes magit as fast in GUI emacs than it is in Big thanks to everyone! |
Hey all, I've updated the patch with some fixes from the mailing list. Also, here are some updated installation instructions until it is merged in: $ brew uninstall emacs-plus
$ brew tap aaronjensen/emacs-plus
$ brew reinstall aaronjensen/emacs-plus/emacs-plus --with-vfork As for benchmarks @jojojames I saw a bigger improvement % wise, but my machine must be faster overall. I went from ~5-6ms to ~1-2ms. Btw, on the bench it takes out some variability (and probably more closely replicates what magit is actually doing) if you run it 100 times (just divide the result by 100 afterwards): (benchmark 100 '(call-process "/usr/bin/true" nil nil nil)) Elapsed time: 0.123614s I don't have saved numbers, but before the patch I was above 0.500s for the same test. |
You're right, it's a significant speedup (rebuilt Emacs off master).
|
This significantly speeds up `call-process` which is used heavily by magit. Ultimately, this makes magit, and probably other things more responsive. This is backported from master a13eaddce2ddbe3ba0b7f4c81715bc0fcdba99f6. See http://lists.gnu.org/archive/html/bug-gnu-emacs/2017-04/msg00201.html and magit/magit#2909
FYI, the patch is now merged to emacs master: And it's available by default via homebrew w/ emacs-plus: |
@tarsius Not what you think, but you may be able to consider this closed now that there shouldn't be a significant difference between mac and linux perf. |
I have added a summary to the manual and updated the original post above to link to it. Thanks again to everyone helping with this! |
What other commits have to be cherry-picked to the That commit by itself applies, but apparently doesn't compile: |
I think it'd be a couple commits. There were changes to expose We could probably figure out exactly which commits are needed, but there could be quite a long thread in that sweater. |
Thanks, I am now linking to this backport. |
Unfortunately, it appears that the performance of |
|
Known issue apparently: https://lists.gnu.org/archive/html/emacs-devel/2021-10/msg02223.html There's a fix in Emacs macport, but not yet on master and it looks like the original conversation died out. |
emacs-plus now has patches in place for 28 and 29. A patch has been proposed upstream, waiting on that. |
For those arriving through searches, emacs-plus both landed and then removed patches to accomodate this, as it was marked as upstreamed: d12frosted/homebrew-emacs-plus#422 Unfortunately the commit messages did not mark the upstream commit(s) which implemented this and I'm unable to find it, but it stands to reason that any up-to-date emacs 28+ package you install on mac should have a fix for this, so no special concern needed by users going forward, regardless of flavor. |
Your second link includes in the PR description the commit to emacs: emacs-mirror/emacs@cc4edea |
...Whoops. Thank you. |
Edit by @tarsius: The cause of this has been found and it has been fixed on Emacs'
master
branch. See this for more information, including how to get a backport of that fix.Edit by @tarsius: The fix is included in Emacs 26.1.
"2.9.0-34-gfee9c17c"
GIT: 2.11.0
magit-stage of a single file with a 5-line diff is perceivably slow, around 1 second.
magit-unstage also suffers from the same perceived slowdown.
Some profiling data:
The amount of time spent for such a frequently-used operation is enough to lead to
annoyance and mental anguish on the part of the user :-]
Also note that I have 30 untracked files and 10 unstaged changes in this repo.
Maybe this plays into magit-refresh/magit-refresh-buffer?
The text was updated successfully, but these errors were encountered: