Emacs has many great packages. If you add tens or hundreds of them to your init.el, you might find the startup gets really slow. If that’s happened, you would want to know what are the culprits of the slowness, and hopefully to address them.
Find out slow packages
To know which packages are slow if you use use-package, you can set in your init.el just after enabling use-package:
(setq use-package-compute-statistics t)
After restarting Emacs, you can run M-x use-package-report. It shows something like this:
Pakage Status Last Event Time
nerd-icons Configured 08:39:29.201845 0.26
material-theme Configured 08:39:28.859925 0.22
flycheck Configured 08:39:30.311187 0.18
corfu Configured 08:39:29.332115 0.04
flycheck-inline Configured 08:39:30.325567 0.03
blacken Configured 08:39:30.339723 0.03
orderless Configured 08:39:29.295405 0.02
which-key Configured 08:39:28.976554 0.02
Then, you can look into packages that take, for example, more than 0.5 sec. (The above example was after I addressed issues)
In my case, initializing exec-path-from-shell and pdf-tools took 5+ seconds each. No wonder my Emacs was annoyingly slow to start.
Addressing exec-path-from-shell
This package seems to be infamous for its slow loading. My solution was not to use it.
As I don’t frequently update PATH environment variable, I don’t have to retrieve the value every time Emacs starts. So, I changed from:
(use-package exec-path-from-shell
:ensure t
:config
(when (memq window-system '(mac ns x))
(exec-path-from-shell-initialize)))
to
(when (memq window-system '(mac ns x))
(progn
(setenv "PATH" "/Users/<snip>Toolbox/scripts")
(setq exec-path (split-string (getenv "PATH") path-separator))))
The PATH value was copied and pasted from:
echo $PATH
This alone reduced 5+ seconds. Great!
Addressing pdf-tools
I don’t really remember what I had, but it was something like:
(use-package pdf-tools
:ensure t
:config
(setenv "PKG_CONFIG_PATH" "/opt/homebrew/Cellar/zlib/1.3.1/lib/pkgconfig")
(pdf-tools-install)
(custom-set-variables
'(pdf-tools-handle-upgrades t)))
And I changed to:
(use-package pdf-tools
:ensure t
:config
(setenv "PKG_CONFIG_PATH" "/opt/homebrew/Cellar/zlib/1.3.1/lib/pkgconfig")
:hook (after-init . pdf-tools-install))
By using :hook, use-package delays loading the package. This also reduced 5+ sec.
But, if I reverted the change, somehow it didn’t add 5+ sec to the startup time. Mmmm….
Deferred loading with use-package
If you use use-package, having :init and/or :config: alone doesn’t seem to tell use-package that it should defer loading. In this case, you can add :defer t. But this sometimes doesn’t work.
The manual says :defer t alone is not very useful. I still need to learn more in this area. My strategies for now is to:
- Put
:defer t - Use
:hookand:bind
and see if it affects the package startup time. If it didn’t, I just remove :defer t. For example, I put :defer t to pyvenv config and it worked:
;; pyvenv
(use-package pyvenv
:ensure t
:defer t
:config
(pyvenv-mode 1))
I addressed several slow-loading packages in this way, and I was able to shorten Emacs startup drastically. Now it starts in a few seconds, and doesn’t annoy me anymore. Fast Emacs startup is good for your mental health.