Subject: Re: bug#78766: 100-4000x redisplay slowdown with vscroll>0 and make-cursor-line-fully-visible=t

From: Eli Zaretskii <[email-masked]>
Date: Thu, 12 Jun 2025 09:01:26 +0300

> From: JD Smith <[email-masked]>
> Date: Wed, 11 Jun 2025 19:08:01 -0400
> 
> Users of ultra-scroll noticed significant slowdowns in some situations.  We traced it back to the combination of:
> 
> - vscroll > 0 (ultra-scroll, like pixel-scroll-precision, uses vscroll for its scrolling implementation)
> - make-cursor-line-fully-visible=t
> 
> Note that pixel-scroll-precision disables make-cursor-line-fully-visible, but this leads to partially visible lines causing problems in various other situations (e.g. comint-scroll-show-maximum-output).  So disabling isn't ideal.  
> 
> A simple test (validated in Emacs 30 with NS and mac builds) is attached.  Evaluate the buffer and it will enable make-cursor-line-fully-visible, visit simple.el, then time moving forward to the end of a line with and without non-zero vscroll.  
> 
> This is painfully slow with make-cursor-line-fully-visible=t.  The reported slowdown for simple motion commands like forward-char is 100-4000x.

AFAIU, you are asking Emacs to do the impossible: make the cursor line
fully-visible when the cursor line is at the top of the window, and
therefore _cannot_ be fully visible due to non-zero vscroll.  Am I
right, or did I miss something?

If I'm right, then could you please explain what do you expect in this
situation, and why do you expect this to do anything useful?

I haven't yet step through the code in this case, but my guess is that
the display engine attempts all kinds of measures to make the cursor
line visible, eventually failing, and those attempts are expensive
because the display code was never designed to cope with such a
strange set of conflicting requirements.  In particular, it expects
the partially-visible line situation to happen at the bottom of the
window, not at its top.

> I've profiled the slow case, see attached for the important parts.  As is clear, of the ~8s it took to move to the end of the line (twice), get_next_display_element and set_iterator_to_next are the main culprits (arrived at separately via try_window and partial_line_height) with gui_produce_glyphs contributing. 

These are all normally called functions, so the profile doesn't help
much.  My guess is that some of the functions are called an abnormally
large number of times, but to see that, I need (a) how many times was
each function called, and (b) across how many characters did Emacs
move during the time the profile was collected.  This is so I could
calculate how many times each of the functions was called
_per_character_move_.

Also note that normally, moving the cursor is very fast because it
employs significant redisplay optimizations, which usually cause only
a single screen line to be updated.  The situation you create with
this recipe most probably disables all of those optimizations, and
thus makes the response to C-f much slower.  We could perhaps improve
the situation in some way, but I need to understand why such a strange
combination of display-related knobs is used in the first place, and
what do you expect it to produce and why.

Thanks.