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

From: Eli Zaretskii <[email-masked]>
Date: Sat, 14 Jun 2025 09:59:09 +0300

> From: JD Smith <[email-masked]>
> Date: Fri, 13 Jun 2025 14:04:03 -0400
> Cc: [email-masked]
> 
> 
> 
> > 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.

True, but it's hardly new information.  Problems with slow redisplay
happen not because set_iterator_to_next is slow, but because it is
called too many times, for whatever reasons.  It is those reasons that
are interesting, because the way to make redisplay fast enough is to
eliminate the reasons for those many calls.

> > 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.

Which is why I'm asking why these many calls happen.  What my
intuition tells was described up-thread, but what we need here is
facts: why does Emacs in fact try to redraw this window's characters
so many times?

> > 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.

The way to answer these questions is to step through the code in
redisplay_window and see what it does and why in that case.  If
there's no answer to this question by the time I get enough free time
to do it myself, I will.

> > 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.

This is AFAIU a new display feature that currently doesn't exist.  If
I'm right, it will need addition of new code.

> 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.

That's impossible, because make-cursor-line-fully-visible cannot be
ignored under the current code.  It's a hard requirement, and is very
central to one of the most important goals of the design of the Emacs
display engine: ensure that point is always (fully) visible in the
window.

> > 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".

The way the display engine is designed, it first find a promising
starting-point for displaying the buffer in its window, then tries to
display the window with that start point, then check whether such a
display is successful (and here it checks whether point is in a
fully-visible screen line, among others).  I hope you understand the
conundrum now?

> > 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.  

FWIW, I consider all these weak justifications when you are literally
fighting against the design of the display engine.

> 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.

Yes, "for scrolling through tall images".  That's what vscroll was
designed for, but you are using it for completely different purposes.
Why is it a surprise that it doesn't work well?

More generally, when will authors of Lisp packages understand a simple
truth that the Emacs design principles basically place hard
limitations on what Lisp programs can usefully and efficiently do, and
stop attempts to use the Emacs features way out of their design space?
(No, don't answer that.)