21

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?

Nova
  • 1,059
  • 9
  • 21
  • FWIW, I haven't touched the garbage collection settings, and have never been aware of it during normal operations. – Dan Jul 22 '17 at 14:10
  • I re-posted a link to this thread over on reddit/emacs https://www.reddit.com/r/emacs/comments/6ow208/downside_to_setting_gcconsthreshold_very_high_and/?ref=share&ref_source=link and Eli Z. (a lead developer/maintainer of Emacs) has already posted a comment. There may be additional comments within the next few days. – lawlist Jul 22 '17 at 17:25
  • 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 01:28
  • In any case, assuming you actually have enough RAM to support the chosen value, the obvious downside to doing this is that Emacs may be unresponsive for longer than desirable during gc; but I don't have any numbers (and they would be system-dependent in any case), so I guess you should do some testing and report back? – phils Jul 24 '17 at 01:39
  • @phils: I'm aware that GC makes Emacs unresponsive and collecting only every 1GB would lead to noticable pauses. As I understand it, `gc-cons-threshold` is set so low in part to keep each pause shorter. If the pause occurs during idle time, I don't notice it either. – Nova Jul 24 '17 at 22:23
  • @phils: I have a lot of RAM so luckily the highest I'm willing to use is actually more than I'll ever need. Otherwise I would set `gc-cons-threshold` lower, but still much higher than 800KB. Trying to save less than 1MB at any expense of user experience is just silly on a modern PC. – Nova Jul 24 '17 at 22:27
  • FWIW some time ago I adopted the "increase the threshold for init, and decrease after init" approach (which improved init time enough to make it worthwhile), and I used a 10M value there, which proved more than adequate. Hence 1GB seeming entirely OTT to me. My point about Emacs being unresponsive during GC was about what will happen if/when it hits your limit (whatever it is) in *non-idle* time (which I think you ought to assume may still happen, even if rarely). I think you should pick a value that causes an acceptable delay if it happens in a non-idle context. – phils Jul 24 '17 at 22:28
  • 5
    According to [this post by Stefan Monnier](https://lists.gnu.org/archive/html/help-gnu-emacs/2007-06/msg00243.html): *"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 06:03
  • 2
    @izkon except that the post you linked to dates back to 2007, whilst e.g. [this post](http://bling.github.io/blog/2016/01/18/why-are-you-changing-gc-cons-threshold/), 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. – Nova Jul 24 '18 at 13:18

2 Answers2

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")))
  • Why don't you use `after-init-hook`? – Nova 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. –  Jul 26 '17 at 08:58
  • Can we just do `(setq gc-cons-threshold 100000000)` instead? – alper Aug 13 '21 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.

Bae
  • 212
  • 1
  • 10