TL;DR I have such a huge amount of packages that it's hurting my startup time. If you don't believe that could be the case, read on.
My Emacs startup time is quite small. I don't use use-package
, I just set tons of hooks and autoload
s so that almost all code is deferred. In reality the whole thing is loaded in usually less than half a second, despite it seeming like a crazy mess.
However, over time I noticed that my startup time gets minutely slower, inexplicably. This has eventually gotten to the point where startup time is ≥ 1 second. I finally had enough and I dug into the root of the problem. I eventually commented out my whole ~/.emacs
file and found that startup time was still ≥ 1 second. In fact, it had only shaved off ~0.2
seconds, sometimes even less. Then I tried emacs -q
and found that the startup time was ~0.1
seconds.
Upon examining this section of the Elisp manual, I found out why emacs -q
was reducing startup time so much. Apparently emacs -q
stops Emacs from doing three things at startup:
- loading your init file
- loading your
default.el
file - calling
package-initialize
We've already ruled out my init file, since commenting out my entire ~/.emacs
does almost nothing. I don't use a default.el
file, so that is also ruled out. Which leaves package-initialize
as the culprit for the performance hit.
Why would package-initialize
be taking up so much startup time? That was the first question I asked myself. Aren't I autoloading everything? Well, yes. But that is precisely the problem.
I found this post which explains that "activating" packages consists of reading autoload files and setting load paths. This obviously incurs an I/O penalty when you have many packages because you have many autoload files to read and many paths to set. Unfortunately, without this, the task of managing autoloads falls into the hands of the user. In other words, without letting package.el
crawl the filesystem for autoload files and paths, I would have to manage that myself which could be a tedious and error-prone process.
I would prefer not to go down that road. I currently have 116 packages, with 107 of those from ELPA and 25 of which are dependencies. I am sure that this whopping number is what is degrading my performance so badly. But I am in a quandary because I do not want to remove any of my packages.
Is there any remedy in such a situation to get my lightning startup time back?
Update:
We've started a new thread on the emacs-devel
mailing list about some patches by Stefan Monnier (a description of these patches is here) to solve this problem. Anyone is welcome to test out his patches and give feedback.
Another update:
It seems that Stefan Monnier is either not interested in this issue anymore or he is not getting my messages. I am inclined to believe the former, which is fine, though I would appreciate some kind of response from him if that is the case. Anyway, the code which he has produced for this issue so far works quite well. The most recent patches of his can be found here (for Emacs 25.3) and here (for Emacs master branch). I have seen good improvements on my startup time thanks to his patches and I'm at a point where I'm comfortable with my startup time insomuch that it is as optimized as possible without cutting away features of my customization. I was hoping that these patches would make it into the Emacs mainline at some point, but I guess that I (or someone else) would have to take up the torch for it now, instead of Stefan. We had a bit of a spar on the mailing list about copyright assignment and licensing. I was initially uncomfortable with doing so, but due to some comments from Richard Stallman and others, the copyright assignment may not be as restrictive as I originally thought. Moreover, it may be possible for me to commit my works to the public domain as an alternative to copyright assignment. That sounds like the route I will go down if I choose to try to contribute improvements of Stefan's patches to the mainline.
In any case, thanks Stefan for the patches so far! I hope you will continue to develop these changes, but if not, that's okay and I may continue to develop it at some point. I also thank everyone else who offered insight and contribution to solving this issue.
Yet another update:
Wow, looks like this feature finally landed and will be in Emacs 27. Thanks to Stefan Monnier!
use-package
is the way to go for this.