20

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
    Jul 24 '17 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
    Sep 22 '17 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
    Jul 24 '18 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
    Jul 24 '18 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.
    – Erik
    Jul 24 '18 at 13:18
7

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?
    – Erik
    Jul 24 '17 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
    Jul 26 '17 at 8:58
  • Can we just do (setq gc-cons-threshold 100000000) instead?
    – alper
    Aug 13 at 20:23
-2

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.

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.