Subject: Re: bug#75574: Adaptive read buffering is a pessimization

From: Daniel Colascione <[email-masked]>
Date: Wed, 15 Jan 2025 07:37:10 -0800

On January 15, 2025 6:59:12 AM PST, Eli Zaretskii <[email-masked]> wrote:
>> Date: Tue, 14 Jan 2025 21:42:13 -0800
>> From: Daniel Colascione <[email-masked]>
>> 
>> The adaptive read buffering code delays reads in the hope that we can read in buffer chunks if we wait a
>> little bit between reads from a process producing a lot of output. Sounds good. Doesn't work. The attempted
>> optimization reduces performance in various scenarios and causes an 8x regression in performance for me
>> in flows involving mixtures of big and small reads.
>> 
>> With adaptive reading, we increase the read delay every time we get a short read and decrease it when we
>> get a full buffer of data or do a write.  The problem is 1) that there are legitimate flows involving long
>> sequences of reads without an intervening write and 2) reads (especially from PTYs) may *never* report a
>> full buffer because the kernel limits the maximum read size no matter how big the backlog is. (For example,
>> the Darwin kernel limits PTY (and presumably TTY in general?) reads to 1024 bytes, but the default Emacs
>> read size is 64k, so we never recognize a signal that we should reduce the read delay.
>> 
>> I'd suggest just deleting the feature. It's not worth the complexity and edge cases, IMHO.
>> 
>> If that's not an option, I'd suggest detecting bulk flows by doing a zero timeout select() after we're tempted to
>> increase the delay and actually increasing the delay only when that select times out.
>> 
>> Just tweaking the maximum read size probably isn't a good idea: it's an implementation detail and can
>> change with time and over the types of FD from which we read.
>
>AFAICS, this feature can be disabled by setting
>process-adaptive-read-buffering to the nil value, either globally or
>let-binding it around start-process.  Does that solve your problems,
>and if so, can we conclude that Emacs allows both using the feature
>and disabling it.

Well, I mean, Emacs is free software, so in theory users can disable any bug with enough elbow grease, even ones we don't know about. I have the radical idea that it shouldn't be slow by default and that users shouldn't have to manually disable known bugs.

>AFAIR, in some situation this was really useful, otherwise we wouldn't
>have set it to t by default.

Plenty of questionable things sound like good ideas at the time.

>Or maybe we should introduce a new special value of
>process-adaptive-read-buffering, which would be equivalent to nil when
>reading from PTYs, if that case can never benefit from these delays?

Even if you were to limit the adaptive reading to pipes and sockets, you'd still have the problem with trying to guess the maximum read size even if you limit the mechanism to pipes. It's just broken. I don't see a reason to continue to maintain the complexity. What are we saving? A few context switches, maybe? Machines are fast enough that we don't need to make a queue form for the sake of batching.