40

What are some basic things I could do to reduce the start-up time?

Is there anything in particular I should pay attention to, for that matter?

Note: Startup time can be mitigated by starting Emacs less often (once per session) and opening files in a running instance. This question is about minimizing the startup time, for the session start or any other time when starting Emacs is necessary.


See also the same question answered on Stack Overflow, with question and answer scores over 50 and 30-some "favorite" bookmarks. Good answers here should go beyond what is available on Stack Overflow.

  • 1
    I'd love to have data on this, but my guess is that for most users there is one or two packages that makes for the bulk of the startup time. In my case it was helm. Note that if you use helm you can't really postpone its initialization, you do want it ready to use right away. I switched to ivy and that brought my start time down from around 12 seconds to less than a second. I even stopped using the server/client setup. (By the way, I didn't switch to reduce startup time, that was just a nice side benefit.) – Omar Nov 3 '15 at 20:45
43

Here's my points on reducing emacs-init-time, this does not cover things like using a daemon or the server, it goes without saying that you should rarely ever close emacs.

Don't:

  • Don't require packages in your init, if the package doesn't have proper autoload cookies, ensure you set up autoloads on the entry commands. So if the first time you use package foobar will be by calling foobar-mode and foobar was did not come pre-autoloaded you'll need something like this:

    (autoload 'foobar-mode "foobar")
    

    this will allow you to call foobar-mode even when the foobar package has not yet been loaded. This way foobar won't be loaded until you actually call foobar-mode

  • Don't run package-refresh-contents if you don't need to install packages on startup. If you're init is setup to auto install missing packges, consider setting up a command line arg for you to specify when auto installing should occur.

  • Like above, don't do anything network related.
  • Don't load your desktop on init unless you really really want to.

Do

  • Do use something like use-package to manage your packages. This makes it easy to specify what to require, what to load later, what autoloads what and makes profiling your init on a package by package basis easy.

  • Do know the difference between loading a theme and enabling it. In short, you can load as many as you want, but make sure you are not enabling more than one. Ideally, only load and enable one theme. load-theme needs an optional arg to prevent the enabling of the theme. It can be easy to accidentally enable multiple themes which is slow and ugly during startup.

  • Do cheat: There are often large global modes that you'll want to load on init, things like undo-tree, autocomplete, ido-mode etc. Ensure the entry functions have autoloads setup then start idle timers in your init to load the packages. I do this will undo-tree-mode, ido and others and never notice a delay because by the time I actually need to use them, they are already loaded.

    Update: use-package has changed a bit, read the official readme before you start using the timer features.

    For example: if you wanted to slightly delay the loading of global-undo-tree-mode you could put this in your init:

    (run-with-idle-timer 1 nil (lambda () (global-undo-tree-mode t)))
    

    Now your init can continue along happily and global-undo-tree-mode won't actually be activated until after everything else is ready to go and you are behind the wheel.

    use-package has support for this kind of behavior built in using the :idle keyword. Here is the undo-tree config from my .init.el:

    (use-package undo-tree
      :idle (global-undo-tree-mode 1)
      :bind (("C-c j" . undo-tree-undo)
             ("C-c k" . undo-tree-redo)
             ("C-c l" . undo-tree-switch-branch)
             ("C-c ;" . undo-tree-visualize))
      :ensure t)
    
  • Do profile your init, it's always surprising to see where the real slow downs are. profile-dotemacs.el is an incredible tool that I've used to help me take my init down from ~6 seconds to < 1 second.

A well configured use-package init can be incredibly fast. I do not byte-compile my init and it uses use-package to configure 95 packages and starts up in < 1 second.

  • 7
    "Do profile your init, it's always surprising to see where the real slow downs are." Spoiler alert, it's that (require 'org) line. :-) – Malabarba Oct 17 '14 at 14:05
  • @Jordan, could you please extend a bit on how you ensure the entry functions have autloads setup then start idle timers in your init to load the packages, in particular for undo-tree-mode? Thanks. – Francisco Dibar Oct 17 '14 at 19:47
  • @FranciscoDibar I have updated my post with examples. – Jordon Biondo Oct 17 '14 at 19:55
  • 2
    I use ido-mode immediately, C-x C-f or M-x for smex is the first thing I almost always when I open emacs, and I've never noticed a problem. Also if you want to undo something within a second of opening emacs... Well I have nothing to say about that. If you're really that concerned, try it out yourself, or just use a non-idle timer or after init hook. – Jordon Biondo Oct 17 '14 at 22:54
  • 1
    The idle timer suggestion is useful. A slightly shorter loading syntax is (run-with-idle-timer 1 nil #'global-undo-tree-mode)'. If the function you are loading takes parameters you can just provide them after the #'` command. – Andrew Swann May 31 '16 at 12:29
8

Something that recently popped up on emacs reddit: decrease the number of garbage collection invocations by putting this near the beginning of your init file:

(setq gc-cons-threshold 50000000)

(add-hook 'emacs-startup-hook 'my/set-gc-threshold)
(defun my/set-gc-threshold ()
  "Reset `gc-cons-threshold' to its default value."
  (setq gc-cons-threshold 800000))

In the example above, the GC is invoked every ~50MB (instead of the default of ~800kb), which seems sensible on a modern system with plenty of RAM.

  • 1
    Except that value is (a) probably way higher than you need (I see no difference with one tenth of that); and (b) evidentially not a value you want to persist beyond start-up, because a large GC threshold equates to longer delays whenever GC happens. If you set it high for init, set it back lower again after init. I think that emacs-startup-hook is a good place to do that. – phils Sep 14 '15 at 22:11
  • 1
    @phils Thanks! (a) On my setup, 50Mb gives the minimal number of GCs (and the minimal startup time). If I go as low as 10Mb, the difference is noticeable/measurable (although it doesn't change much in practice...) (b) good idea, thanks. I edited the post to reflect your comment. – ffevotte Sep 15 '15 at 14:39
6

The time you spend optimizing your startup time will likely be greater than all the extra time you would have otherwise waited for Emacs to start up.

At the moment I make 25 require calls in my init file so that Flycheck can find spelling errors in my code. My startup time is...

$ time emacs --eval '(save-buffers-kill-terminal)'

real    0m2.776s
user    0m2.305s
sys     0m0.148s

Also, on my system, time emacs -Q --eval '(save-buffers-kill-terminal)' has a real of 0m0.404s. The theoretical maximum amount of time I can save is 2.3 seconds.

Say that I spend an hour making all matter of optimizations to my init file. (I won't count the additional 15-30 minutes spent on a later date trying to figure out why my changes weren't taking effect due to my init file being byte-compiled.) (I also won't count the time that Flycheck would have saved me in the debugger if I hadn't removed the require calls.) There are 3600 seconds in an hour, so if I managed to save the whole 2.3 seconds, my investment in time would only pay off after 1565 startups.

Assuming I restarted Emacs 3 times a day, every day, it would take a year and half for that investment to pay off. If I left the same Emacs instance running for days at a time (as I often do), I'd probably only restart 2-5 times a week, in which case it would take 6 to 15 years for that investment to pay off.

I'm being generous, because you are likely to spend more than an hour optimizing your startup, and you probably won't save the maximum theoretical number of seconds.

  • 12
    But you'll potentially be happier. – phils Sep 14 '15 at 3:24
  • 2
    That may be true for one person, but the whole point of StackExchange is about sharing. What about a trick that takes 30 minutes for one person to find, but shaves 1 second off the startup time of dozens of people? Would you still consider it a bad investment? – ffevotte Sep 14 '15 at 15:35
  • @phils Ironically, I considered saying the same thing, but in support of my own viewpoint! "Before you groan about your startup time, think to yourself, 'I'm glad I did not waste time optimizing this!'" – Jackson Sep 14 '15 at 17:51
  • @Francesco And this post is my trick that saves dozens of people time. – Jackson Sep 14 '15 at 17:52
  • @Jackson I still don't agree with you on this particular issue, but at least now I see your point. Thanks :) – ffevotte Sep 14 '15 at 18:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.