This week I finally got around to upgrading my system and my Emacs packages, including EXWM. Everything went fine, except for one problem: every time I loaded a XKB keymap, EXWM would hang for 10–20 seconds, with CPU usage going up. I opened an issue on the EXWM repository, but I decided to investigate a bit more.
After learning the basic commands for profiling Emacs Lisp code, I started the profiler (M-x profiler-start), loaded a new keymap, and generated a report (M-x profiler-report). It turned out that 73% of the CPU time during the hangup was spent on garbage collection. I tried the profiler again, now starting it in cpu+mem mode rather than the standard cpu mode. From the memory report, I learned that Emacs/EXWM was allocating around ~500MB of memory during the keyboard loading (!), apparently handling X MapNotify events.
I did not go far enough to discover why so much memory was being allocated. What I did discover though is that Emacs has a couple of variables that control the behavior of the garbage collector.
gc-cons-threshold determines how many bytes can be allocated without triggering a garbage collection. The default value is 800000 (i.e., ~800kB). For testing, I set it to 100000000 (i.e., ~100MB). After doing that, the keyboard loading freeze fell from 10–20s to about 2–3s. Not only that, but after setting it near the top of my init.el, Emacs startup time fell by about half.
Now, I've seen people warn that if you set gc-cons-threshold too high, Emacs will garbage collect less often, but each garbage collection will take longer, so it may cause some lag during usage, whereas the default setting will cause more frequent, but less noticeable garbage collections (unless you run code causing an unusually large number of allocations, as in this case with EXWM). However, I have been using it set to 100MB for a couple of days now, and I haven't noticed any lag; I just got a faster startup and less EXWM hangup. It may well depend on your Emacs usage patterns; you may try different values for this setting and see how it works for you.
Another recomendation I have seen elsewhere is to set gc-cons-threshold high and then set an idle timer to run garbage-collect, so Emacs would run it when idle rather than when you're using it, or setting a hook so it would run when unfocused. I did not try that, and I suspect it wouldn't work for my use case: since Emacs runs my window manager, I'm pretty much always using it, and it's never unfocused anyway. Yet another recommendation is to bind gc-cons-threshold temporarily around the allocation-intensive code (that comes from the variable's own documentation), or to set it high on startup and back to the original value after startup is finished. Those don't work easily for the XKB situation, since Emacs does not know when a XKB keymap change will happen (unless I wrote some Elisp to raise gc-cons-threshold, call XKB, and set it back after a while, which is more complicated than necessary).
Copyright © 2010-2023 Vítor De Araújo
O conteúdo deste blog, a menos que de outra forma especificado, pode ser utilizado segundo os termos da licença Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International.
Powered by Blognir.