23

I added the following two lines to the top of my init.el:

(setq gc-cons-threshold (eval-when-compile (* 1024 1024 1024)))
(run-with-idle-timer 2 t (lambda () (garbage-collect)))

That means that instead of collecting garbage every 800kb of allocated memory, Emacs does so when idle, i.e. when the pausing does not bother me. (It also collects after allocating 1GB of memory, but I don't think that will happen).

This improved my startup time by about two thirds. In theory, it should also improve performance in general. Are there any downsides to this approach?

11
  • 1
    In principle you shouldn't set gc-cons-threshold any higher than you're willing to actually hit at any given time, because you ought to assume that you will actually hit that value from time to time (after all, who knows how much garbage might be accrued by some unexpectedly-enthusiastic non-idle task). I don't see a particular problem with triggering gc with an idle timer, but I think setting the threshold for non-idle gc as high as this seems OTT, and my impression is that the value was probably chosen as being "higher than I'll ever need" rather than "the highest I'm willing to use".
    – phils
    Commented Jul 24, 2017 at 1:28
  • 5
    According to this post by Stefan Monnier: "Better not touch it. In Emacs-22 we introduced gc-cons-percentage which provides the same benefit as increasing gc-cons-threshold but without the drawbacks. And without having to fiddle with it. I.e. I'd recommend users to remove any gc-cons-threshold settings from their .emacs."
    – izkon
    Commented Sep 22, 2017 at 6:03
  • 2
    @izkon except that the post you linked to dates back to 2007, whilst e.g. this post, where someone actually have experimented — and changing the threshold did make the difference — dates back to 2016. So either it regressed, or the workaround just have never worked well.
    – Hi-Angel
    Commented Jul 24, 2018 at 12:10
  • 1
    @Erik I think you can replace (eval-when-compile (* 1024 1024 1024)) with most-positive-fixnum (please do so, I'm pretty sure everybody who comes across your question copies your code into their config).
    – Hi-Angel
    Commented Jul 24, 2018 at 12:12
  • 3
    @Hi-Angel I don't think that is a good idea. If Emacs actually allocates huge amounts of memory without becoming idle, it should gc instead of continuing to allocate until the system has to swap or even runs out of memory entirely. If anything, 1GB is already too high.
    – Nova
    Commented Jul 24, 2018 at 13:18

2 Answers 2

8

As far as I know, if you have the RAM, it's okay, but if Emacs ever did hit really high usage before GC'ing, it might take a long time. I'm not sure exactly what Eli means; ISTM that if you have enough memory, it should be okay, but he's the expert here.

Having said that, I've used these lines in my init file for a while now, and it helps reduce startup time without making the changes permanent:

;;;;; Startup optimizations

;;;;;; Set garbage collection threshold

;; From https://www.reddit.com/r/emacs/comments/3kqt6e/2_easy_little_known_steps_to_speed_up_emacs_start/

(setq gc-cons-threshold-original gc-cons-threshold)
(setq gc-cons-threshold (* 1024 1024 100))

;;;;;; Set file-name-handler-alist

;; Also from https://www.reddit.com/r/emacs/comments/3kqt6e/2_easy_little_known_steps_to_speed_up_emacs_start/

(setq file-name-handler-alist-original file-name-handler-alist)
(setq file-name-handler-alist nil)

;;;;;; Set deferred timer to reset them

(run-with-idle-timer
 5 nil
 (lambda ()
   (setq gc-cons-threshold gc-cons-threshold-original)
   (setq file-name-handler-alist file-name-handler-alist-original)
   (makunbound 'gc-cons-threshold-original)
   (makunbound 'file-name-handler-alist-original)
   (message "gc-cons-threshold and file-name-handler-alist restored")))
3
  • Why don't you use after-init-hook?
    – Nova
    Commented Jul 24, 2017 at 23:06
  • 4
    Because that would run immediately after initialization, which might make the user wait for GC. By using an idle timer, it can run when the user's not using Emacs.
    – user3871
    Commented Jul 26, 2017 at 8:58
  • Can we just do (setq gc-cons-threshold 100000000) instead?
    – alper
    Commented Aug 13, 2021 at 20:23
-1

https://bling.github.io/blog/2016/01/18/why-are-you-changing-gc-cons-threshold/ suggests leaving the default value for gc-cons-threshold and then finding which packages perform badly and increasing the value only for those parts.

The example includes disabling gc while the minibuffer is open.

(defun my-minibuffer-setup-hook ()
  (setq gc-cons-threshold most-positive-fixnum))

(defun my-minibuffer-exit-hook ()
  (setq gc-cons-threshold 800000))

(add-hook 'minibuffer-setup-hook #'my-minibuffer-setup-hook)
(add-hook 'minibuffer-exit-hook #'my-minibuffer-exit-hook)

I originally had this in init.el

;; Don't be so stingy on the memory, we have lots now. It's the distant future.
(setq gc-cons-threshold 20000000)

and https://github.com/lastquestion/explain-pause-mode was showing me gc was taking time in operations I wasn't expecting, most probably because its got to gc 20MB.

I think I'll look into a more tailored approach to this variable rather than a one size fits alls.

1
  • Could people who are downvoting this answer point out what they think is wrong? Commented Feb 1 at 19:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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