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

From: JD Smith <[email-masked]>
Date: Fri, 13 Jun 2025 14:04:03 -0400

> On Jun 13, 2025, at 11:16 AM, Eli Zaretskii <[email-masked]> wrote:
> 
>> From: JD Smith <[email-masked]>
>> Date: Fri, 13 Jun 2025 10:07:05 -0400
>> 
>> Quick followup.  I was able to instrument the single function `set_iterator_to_next' to track total call count and distribution of call times of this core function.  Since the instrumentation slowed the test down so much, I profiled moving (forward-char) by just 3 chars with redisplay in a partially visible top line, using the slow setting of make-cursor-line-fully-visible=t.  
>> 
>> The total call count per character moved in the slow case is enormous: >800K.
> 
> Thanks, but this doesn't really add any useful info.

It shows that the iterator movement is not in and of itself slow, but that it is called many times per character movement, which was your request before.  But I agree, it's certainly not the full story.

> set_iterator_to_next is too low-level to explain what's going on.  It
> is expected that it will be called many times, but the question is
> why?  

I can see that, but on the other hand, it would be strange to expect it to be called 2850x as many times per character movement when a setting is toggled from nil to t.  That seems to me quite excessive.  Your intuitions here may be better.

> And the answer to that is at higher levels, at the level of the
> functions called by redisplay_window.
> IOW, if we call set_iterator_to_next so many times, we either (a)
> redraw the entire window many times, or (b) redraw some small subset
> of the window's lines even more times.  Which one(s) of these actually
> happen and why is the interesting question.

I'm happy to perform call count and duration stats for other functions if you want to suggest some, but it sounds like you may be better positioned to quickly drill down on this.

> And you still haven't explained to me what you want Emacs to do when
> you set vscroll > 0 (which necessarily makes the top-most screen line
> partially-visible) and ... make-cursor-line-fully-visible = t

I'm agnostic.  Expanding on what I said before, Emacs could either:

1. Prevent the cursor from landing on a partially visible top line.  If it tries to go there, adjust window-start.

2. Keep the current behavior, allowing the cursor to fall on such a line, but eliminate the ~3000x slower path that must be lurking there.

I suspect #2 means effectively ignoring make-cursor-line-fully-visible = t when the cursor is on the top line.

If I got to pick, I'd have a small preference for #1, but I don't have an appreciation for what either would involve in terms of a fix.  Hence the agnosticism.

> These two contradict one another, so you basically ask Emacs to square the circle.

I don't see the inconsistency.  These two settings together do not imply a desire for the cursor to end up on the partially-visible line.  The setting is not called "make-top-line-fully-visible".

> You also haven't explained why using the solution of
> pixel-scroll-precision-mode is not good for your mode.

As I mentioned earlier:

> this leads to partially visible lines causing problems in various other situations (e.g. comint-scroll-show-maximum-output).  So disabling isn't ideal.  

Disabling make-cursor-line-fully-visible leads to... unwanted partially visible cursor lines at the bottom of the window (presumably the reason it exists to begin with).

It doesn't affect scrolling, so this slowness bug is AFAIU the only reason to disable it.  My mode isn't central to this story, other than the fact that it revealed the problem.  

Note that make-cursor-line-fully-visible = t is the default, and vscroll>0 can readily occur via line-move across tall images, no pixel-scrolling needed.  From simple.el:

	    ;; If we moved into a tall line, set vscroll to make
	    ;; scrolling through tall images more smooth.