The display-line-numbers-mode and global-display-line-numbers-mode modes render line numbers in the margin of an Emacs window. Line-number display is implemented in the core C display engine, avoiding the performance cost of legacy Elisp overlays like linum-mode. Despite the native C implementation, and even after configuring Emacs scrolling for better usability, specific configurations can still trigger expensive Lisp evaluations during the interactive command loop, vertical scrolling, and buffer initialization.
These settings avoid unnecessary width calculations and initialization scans.
Speed comparison across line-number types
The display-line-numbers-type variable specifies the type of line numbers activated by display-line-numbers-mode and global-display-line-numbers-mode. It accepts three values:
- Fastest:
t(Absolute): Displays the absolute line number corresponding to the physical buffer line. This is the fastest rendering method because the values are static and do not require recalculation when the cursor moves. - Fast:
'relative: Displays the line number relative to the current cursor position (point). This is slightly slower than absolute line numbering because the display engine must recalculate the distance for every visible line on each vertical cursor movement. - Slow:
'visual: Displays relative numbers based on visual screen lines, accounting for wrapped text. This is the slowest rendering method because it forces the display engine to compute line wrapping layout before assigning numbers.
Performance also depends on the buffer contents and display configuration. For example, using 'visual numbering can degrade scrolling performance in buffers with long logical lines and word wrapping enabled (when truncate-lines is set to nil), because the display engine must calculate layout metrics to determine screen-line boundaries.
I recommend choosing the numbering type based primarily on the desired behavior, not just performance. For instance, even though absolute line numbering is the fastest, I prefer 'relative numbering. Relative numbers show the distance from the current line, which can make vertical navigation easier, especially with numeric prefixes such as C-u 15 C-n in vanilla Emacs or 15j in Evil mode.
;; Enable relative line numbers globally
(setq display-line-numbers-type 'relative)
Disabling display-line-numbers-grow-only
Avoid setting display-line-numbers-grow-only to a non-nil value, as doing so adds display-line-numbers-update-width to the buffer-local pre-command-hook. (The display-line-numbers-update-width function checks the required line number width before each command and increases the display-line-numbers-width variable when necessary.)
Leaving this variable nil avoids that additional Lisp function call before each command:
;; Do not attach the line number width update function to pre-command-hook
(setq-default display-line-numbers-grow-only nil)
Adjusting the display line numbers width
The display-line-numbers-width variable specifies the minimum width of the line-number area. If the required width exceeds the specified value, Emacs can allocate more space.
(setq-default display-line-numbers-width 3)
This sets a minimum width for the line-number area. Without a predefined width, the display engine can adjust the margin width as line numbers grow during vertical scrolling, such as when the line number changes from 99 to 100.
Disabling the initial line-count scan
The display-line-numbers-width-start variable controls whether Emacs calculates an initial width based on the number of lines in the buffer.
Avoid setting display-line-numbers-width-start to a non-nil value because Emacs then scans the entire buffer when initializing the mode.. When enabled, this setting calls a synchronous (count-lines (point-min) (point-max)) evaluation to compute the total line count before establishing the initial margin width.
For large buffers, keeping this disabled avoids that initialization-time count-lines operation:
;; Do not calculate the initial line number width from the buffer's line count
(setq-default display-line-numbers-width-start nil)
Good advices, i've never noticed this myself as i don't use line-numbers.
On the same subject, i use hl-line-mode, and disabling it completely changed org-mode scrolling lag, especially when an image is present in the file, some images more than other would cause heavy lag even in emacs -q.
hl-line.el code seems old and there's a bunch of stuff hooked to post-command-hook... Advicing its functions a bit helped but hopefully we'll see something upstream.