As an aside, if it is safe to assume that the faces are not going to change between digits of the line number, perhaps this change would be a gain.
It moves merging the tick faces outside the lnum_buf loop, and it is done only for the right line numbers and when not beyond_zv.
diff --git i/src/xdisp.c w/src/xdisp.c
index ad73981c1d..2d79a42270 100644
--- i/src/xdisp.c
+++ w/src/xdisp.c
@@ -22657,6 +22657,21 @@ maybe_produce_line_number (struct it *it)
int width_limit =
tem_it.last_visible_x - tem_it.first_visible_x
- 3 * FRAME_COLUMN_WIDTH (it->f);
+
+ /* Select face for tick line numbers, if needed */
+ int tick_face_id = -1;
+ if (!beyond_zv)
+ {
+ if (display_line_numbers_major_tick > 0
+ && (lnum_to_display % display_line_numbers_major_tick == 0))
+ tick_face_id = merge_faces (it->w, Qline_number_major_tick,
+ 0, DEFAULT_FACE_ID);
+ else if (display_line_numbers_minor_tick > 0
+ && (lnum_to_display % display_line_numbers_minor_tick == 0))
+ tick_face_id = merge_faces (it->w, Qline_number_minor_tick,
+ 0, DEFAULT_FACE_ID);
+ }
+
/* Produce glyphs for the line number in a scratch glyph_row. */
for (const char *p = lnum_buf; *p; p++)
{
@@ -22671,14 +22686,8 @@ maybe_produce_line_number (struct it *it)
? this_line == 0
: this_line == it->pt_lnum))
tem_it.face_id = current_lnum_face_id;
- else if (display_line_numbers_major_tick > 0
- && (lnum_to_display % display_line_numbers_major_tick == 0))
- tem_it.face_id = merge_faces (it->w, Qline_number_major_tick,
- 0, DEFAULT_FACE_ID);
- else if (display_line_numbers_minor_tick > 0
- && (lnum_to_display % display_line_numbers_minor_tick == 0))
- tem_it.face_id = merge_faces (it->w, Qline_number_minor_tick,
- 0, DEFAULT_FACE_ID);
+ else if (tick_face_id >= 0)
+ tem_it.face_id = tick_face_id;
else
tem_it.face_id = lnum_face_id;
if (beyond_zv