Recently, I noticed that when I open a large number of buffers in Emacs (for example, all 1000+ files in a Rust project), switching between tabs using tab-line
becomes noticeably sluggish. I suspected a performance issue somewhere.
After conducting a thorough investigation using (profiler-start)
and (profiler-report)
, I obtained the following CPU usage statistics:
3344 84% - redisplay_internal (C function)
2665 67% - eval
2608 66% - tab-line-format
2607 66% - tab-line-tabs-fixed-window-buffers
2520 63% - #<byte-code-function 337>
2507 63% - seq-position
2489 63% - seq-do
2485 63% - mapc
2130 54% - #<byte-code-function 3A4>
623 15% equal
2 0% throw
12 0% make-closure
84 2% + tab-line-tabs-window-buffers
1 0% + tab-line-cache-key-default
48 1% + mode--line-format-right-align
5 0% + breadcrumb--header-line
2 0% + if
1 0% unless
Upon identifying the corresponding code, it became clear that the performance bottleneck was within the tab-line-tabs-fixed-window-buffers
function:
|
|
When old-buffers
is large, using seq-position
within a sort
results in an O(N^2) time complexity—a significant performance problem.
The seq-position
implementation essentially looks like this:
|
|
Finding an element’s index requires nearly traversing the entire list, which is inefficient. To resolve this issue, I used a space-for-time optimization by caching the list’s indices in a hash table.
Here’s the improved function:
|
|
By caching buffer indices in a hash-map
, the function now handles any number of buffers without perceptible slowdowns.
I submitted this patch to bug-gnu-emacs
, and the upstream maintainer confirmed it as the right fix: https://lists.gnu.org/archive/html/bug-gnu-emacs/2024-07/msg00332.html.
This is my first contribution to GNU/Emacs, and from version 31.0.51 onwards, my code will be included—what an exhilarating experience!
Interestingly, Richard Stallman commented on my thread, although he later mentioned it was a mistake: https://lists.gnu.org/archive/html/bug-gnu-emacs/2024-07/msg00380.html.