Why is Emacs/Doom slow?

Emacs scrolling like molasses? Extreme latency while you type? The first-file-load pause taking too long?

8 Likes

Something folks fresh off the boat from other editors will notice is that Emacs has a low threshold for performance issues. What’s worse, this experience is inconsistent across languages, operating systems, and versions of Emacs. It takes little to grind it to a halt.

The sad facts are:

  • Emacs struggles with high DPI/Retina/4k+ displays,
  • Emacs is inherently slower on MacOS and worse on Windows (especially if anti-virus/malware is involved).
  • Emacs has a problem with large (2-10+mb, depending on the language) or long (400+ character lines) files.
  • Some combinations of compositors, gpu settings, or font choices (especially fonts with poor unicode support) can make Emacs chug inexplicably.
  • Some LSP servers are immature and may crash or freeze Emacs, by inundating your system with file watchers and/or consuming all of your CPU/memory.

And that’s before we factor in plugins and unoptimized major mode fontification (i.e. syntax highlighting), let alone issues with your hardware or personal configuration.

What’s left is an unfortunate, but needed, adjustment new users must undergo when adopting Emacs. Doom inherits this curse and does its best to mitigate it without sacrificing the staple features of modern text editors. Still, cases where Emacs is just slow are unavoidable, and it’s trivial to push it to that point by opting into our most expensive features — they are disabled by default for that reason.

In the end, it’s a balancing act that Doom can’t resolve for you, beyond a certain point.

That’s not to say that you have no options, but those depend on the source of the problem. The profiler can help identify bottlenecks if it originates inside Emacs, but if it doesn’t more detective work is needed.

Is it my environment?

A misconfigured system or shell—or misbehaving hardware—can slow Emacs down. Here are some examples:

  1. A rogue compositor preventing Emacs from redrawing as often as it should. Compton/picom have been known to do this. Try disabling them.

  2. If you’re not sporting SSDs or are using old platter drives, they could explain unusual load times (though, not by much, by itself – you may want to investigate the possibility your harddrives are failing).

  3. Anti-{virus,malware}? This can easily add seconds to Emacs’ file IO. Try to whitelist ~/.emacs.d , ~/.doom.d , and Emacs’ site files if possible (usually in /usr/share/emacs/site-lisp/).

  4. If your gpu has vsync on or you’re experiencing flickering, perhaps (add-to-list 'default-frame-alist '(inhibit-double-buffering . t)) added to ~/.doom.d/config.el will help.

  5. If you don’t have gpu drivers installed, install them. Performance complaints from folks with hiDPI displays without gpu drivers are uncommon, but they happen.

  6. Do you have expensive code in your shell dotfiles? Each time Emacs consults an external process it may be spinning up your shell config with it, which can add up. For example, pyenv, rbenv, nodenv, nvm, etc. — these environment managers can perform expensive operations when initializing ({py,rb}env, for instance, take a lot of time to rehash its shims. Pass --no-rehash to their init calls to speed up their initialization).

    This has a pronounced effect on processes that Emacs may consult multiple times per second, like ripgrep (rg), git, or fd.

Is it my Doom config?

Another possibility is a slow config. Are you eagerly loading too many packages at startup? Perhaps an otherwise innocuous setting or plugin doing more work than it needs to? It’s difficult to guess, but here are some suggestions:

  1. Disable idle code completion (i.e. completion-as-you-type). Each time code completion occurs and a popup appears, Emacs has likely consulted one or more external servers to acquire those completion candidates. A slow server or many backends working together can make this very expensive – in some languages more than others.

    If company-idle-delay is set to an especially low value, these backends can be triggered rapidly, causing noticeable latency while typing. Add (setq company-idle-delay nil) to ~/.doom.d/config.el to make code completion manual (on C-SPC, by default). That way, you only need to suffer that slowness once, when it is needed.

  2. Disable some of Doom’s slowest modules. The biggest offenders tend to be: :ui tabs, :ui indent-guides, :ui ligatures, :editor word-wrap and :ui vc-gutter.

  3. Disable particularly slow packages or modes. There are too many to list, but some plugins tend to cost more than others; particularly, plugins that operate on indentation (e.g. aggressive-indent), symbol substitution (e.g. org-superstar/org-fancy-priorities), or introduce GUI elements (centaur-tabs).

  1. Turn off line numbers: (setq display-line-numbers-type nil). It’s known to slow down scrolling, in particular.

  2. Turn off the “prettification” of org-mode, by disabling the +pretty flag on the :lang org module (if you have it on).

    You can go to an extreme by disabling more of its eye-candy:

    ;; add to ~/.doom.d/config.el
    (after! org
      (setq org-fontify-quote-and-verse-blocks nil
            org-fontify-whole-heading-line nil
            org-hide-leading-stars nil
            org-startup-indented nil))
    
  3. Change your font(s). Emacs struggles to display some unicode characters with certain fonts. It sometimes struggles to display multiple fonts at once – as is the case if your primary font doesn’t support certain symbols (Emacs will fall back to another font to try to display them, which appears to be more expensive on some systems than others – or with some fonts, more than others).

  4. Replace the :ui modeline module with :ui modeline +light. The doom-modeline can be unpredictably slow in some edge cases. The +light variant is a work-in-progress replacement aiming to provide a lighter (albeit less featureful) mode line.

  5. Check out “Common configuration anti-patterns”.

Is it my workflow?

Other editors may seem blazingly fast in some scenarios. This is an invitation for unrealistic expectations in Emacs. For example, one thing Emacs is bad at is scrolling rapidly, one line at a time. E.g. If you’re mashing j or C-n to scroll, you’re going to have a bad time (depending on the major mode). Don’t mash j to scroll. There are better options:

Evil users can scroll long distances with C-d and C-u, for instance, or jump directly to matches with evil-easymotion under gs. Otherwise, use search mechanisms to move around, like isearch (C-s) or evil-search (/).

Is it Emacs?

If all else fails, maybe Emacs is just slow™. But don’t lose hope, you still have some options:

  1. If you’re on Emacs 27.x, upgrade to 28.1 (with native compilation). Native-compilation promises a significant improvement in Emacs runtime performance. There are packages available for Arch Linux, Guix, Nix, and MacOS – and likely others. You’ll find more information on the subject on EmacsWiki.

  2. Turn on M-x so-long-minor-mode. This is a minor mode that disables non-essential functionality and can be used to temporarily view files that would be too slow otherwise. M-x so-long-mode is its extreme version; it turns off everything, including syntax highlighting.

    This isn’t a permanent solution to be used everywhere, but can help on one-off edits in big/long files.

Is it Doom?

All this isn’t to say that Doom is free of defect. There are simply too many ways things can go wrong. That said, if you’ve ruled out everything else, then it may be worth reporting it in #support:perf. It may be a bug!

10 Likes