{"author":"zdw","children":[{"author":"wpollock","children":[{"author":"usefulcat","children":[],"created_at":"2026-11-05T18:12:38.000Z","created_at_i":1793902358,"id":50382322,"options":[],"parent_id":50381988,"points":null,"story_id":50380268,"text":"This is true, although neither of those by them selves will achieve what the author is aiming for.","title":null,"type":"comment","url":null},{"author":"cassepipe","children":[],"created_at":"2026-11-05T21:33:11.000Z","created_at_i":1793914391,"id":50384754,"options":[],"parent_id":50381988,"points":null,"story_id":50380268,"text":"If you want to learn more, I liked this blog post:\n<a href=\"https:&#x2F;&#x2F;hmarr.com&#x2F;blog&#x2F;how-stdbuf-works&#x2F;\" rel=\"nofollow\">https:&#x2F;&#x2F;hmarr.com&#x2F;blog&#x2F;how-stdbuf-works&#x2F;</a>","title":null,"type":"comment","url":null}],"created_at":"2026-11-05T17:43:35.000Z","created_at_i":1793900615,"id":50381988,"options":[],"parent_id":50380268,"points":null,"story_id":50380268,"text":"In C, you can use setvbuf to control buffering on stdout.  There are also Linux command line utilities you can use to control buffering, unbuffer and stdbuf.","title":null,"type":"comment","url":null},{"author":"abecedarius","children":[],"created_at":"2026-11-05T18:21:05.000Z","created_at_i":1793902865,"id":50382462,"options":[],"parent_id":50380268,"points":null,"story_id":50380268,"text":"&gt; flush the buffer if it\u2019s been idle a short time<p>I&#x27;ve thought this was the right default for a good while. 50 years ago when Unix was designed, maybe not.","title":null,"type":"comment","url":null},{"author":"Veserv","children":[],"created_at":"2026-11-05T18:47:06.000Z","created_at_i":1793904426,"id":50382808,"options":[],"parent_id":50380268,"points":null,"story_id":50380268,"text":"Yes, basically all buffering should use work-or-time based flushing.<p>Work-based flushing exists to batch work to amortize overhead. It allows you to tune how much time you spend in overhead in a work period. However, this can result in unbounded delay.<p>Time-based flushing targets a worst-case latency in low throughput environments. This ensures you do not get unbounded delays. Interestingly, this also allows you to tune how much time you spend in overhead in a work period.<p>So, with work-or-time based flushing what you are really tuning is how much time you are willing to spend in overhead in a work period and balancing that against latency.<p>As a nit, the article calls this a derivative of Nagle\u2019s algorithm, but it is basically the opposite. Nagle\u2019s algorithm is about batching until acknowledgement with no mention of time. In fact, the lack of time-based flushing in Nagle\u2019s algorithm is why it interacts so poorly with TCP Delayed Ack.","title":null,"type":"comment","url":null},{"author":"cowlevel","children":[],"created_at":"2026-11-05T20:34:11.000Z","created_at_i":1793910851,"id":50384124,"options":[],"parent_id":50380268,"points":null,"story_id":50380268,"text":"This would withhold a line if the writer paused for a while without calling into buffer code. Another case for Golang supremacy?<p>We can also do better than <i>terminals</i>. 7-Zip was the GOAT.","title":null,"type":"comment","url":null},{"author":"edpenz","children":[{"author":"ahartmetz","children":[],"created_at":"2026-11-06T12:58:04.000Z","created_at_i":1793969884,"id":50391797,"options":[],"parent_id":50385046,"points":null,"story_id":50380268,"text":"I strongly suspect that that is the reason for the current behavior. The people behind glibc aren&#x27;t stupid.","title":null,"type":"comment","url":null}],"created_at":"2026-11-05T21:57:19.000Z","created_at_i":1793915839,"id":50385046,"options":[],"parent_id":50380268,"points":null,"story_id":50380268,"text":"One more thing to consider is atomicity of write interleaving to the same pipe. Say you fork two unzips in the terminal like `unzip a &amp; unzip b`. With naive line buffering the outputs mix cleanly, but if aggregating multiple lines per write the chunk size needs to stay below PIPE_BUF for this to stay true.","title":null,"type":"comment","url":null},{"author":"chuliomartinez","children":[],"created_at":"2026-11-05T22:10:41.000Z","created_at_i":1793916641,"id":50385200,"options":[],"parent_id":50380268,"points":null,"story_id":50380268,"text":"I have seen versions of this many times in my life. Unfortunately, that progress report can hurt a lot, is something we devs often miss.<p>From my horror cabinet:\n1. Running count * on all sqlite tables 4x per second to show sync progress.<p>2. Progress bounce animation eating 80% of cpu (windows phone bizarro silverlight)<p>3. Sending myself 800 emails in 10 minutes\u2026<p>The rule is quite simple, we are just bad at it. Computer to human communication should run at human speed.","title":null,"type":"comment","url":null},{"author":"JdeBP","children":[],"created_at":"2026-11-05T23:32:33.000Z","created_at_i":1793921553,"id":50386102,"options":[],"parent_id":50380268,"points":null,"story_id":50380268,"text":"It&#x27;s worth noting that there are more that two kinds of buffering, contrary to the introduction here, and the one that&#x27;s actually used in several C libraries is so-called <i>smart buffering</i>.  This <i>also</i> flushes standard output when standard input needs to do an underlying read and it is reading the beginning of a line.<p>And since musl libc is in the news, it is worth nothing that musl&#x27;s fwrite() is another of those that looks for the <i>last</i> linefeed in the buffer, rather than the first, and so should emit multiple lines in one underlying write().  Although this is only done in fwrite() specifically.<p>* <a href=\"https:&#x2F;&#x2F;git.musl-libc.org&#x2F;cgit&#x2F;musl&#x2F;tree&#x2F;src&#x2F;stdio&#x2F;fwrite.c#n14\" rel=\"nofollow\">https:&#x2F;&#x2F;git.musl-libc.org&#x2F;cgit&#x2F;musl&#x2F;tree&#x2F;src&#x2F;stdio&#x2F;fwrite.c#...</a>","title":null,"type":"comment","url":null},{"author":"phyzome","children":[],"created_at":"2026-11-05T23:35:00.000Z","created_at_i":1793921700,"id":50386133,"options":[],"parent_id":50380268,"points":null,"story_id":50380268,"text":"The number of times I&#x27;ve thought a Python process was broken because it wasn&#x27;t printing anything...","title":null,"type":"comment","url":null},{"author":"jatoo","children":[{"author":"ahartmetz","children":[],"created_at":"2026-11-06T12:56:00.000Z","created_at_i":1793969760,"id":50391772,"options":[],"parent_id":50388997,"points":null,"story_id":50380268,"text":"Yeah, that is a pitfall one shouldn&#x27;t fall into even the first time programming something like that - it&#x27;s pretty obvious, I think? You need to start the deadline timer if it&#x27;s not running, then keep it running - don&#x27;t restart it when more input arrives.<p>Also, this:<p>&gt; Where DEADLINE is something appropriate for human perception, like 100ms or so<p>is way off. You easily notice a hitch of one frame in 60 fps content, and this is comparable. The timer should be something like 1-10 ms. Outputting multiple lines is really fast, so even 1 ms would merge most output into a single write() call. I&#x27;d start with 1 ms and raise it if there&#x27;s a strong reason, which there probably isn&#x27;t.","title":null,"type":"comment","url":null}],"created_at":"2026-11-06T06:43:55.000Z","created_at_i":1793947435,"id":50388997,"options":[],"parent_id":50380268,"points":null,"story_id":50380268,"text":"Doesn&#x27;t the code as written potentially keep extending the deadline each time the deadline_flush is called even if nothing is written to the buffer? So long as you call the flush often enough which would depend on the work in the while loop.","title":null,"type":"comment","url":null}],"created_at":"2026-11-05T15:25:22.000Z","created_at_i":1793892322,"id":50380268,"options":[],"parent_id":null,"points":112,"story_id":50380268,"text":null,"title":"We can do better than line-buffered output","type":"story","url":"https://nullprogram.com/blog/2026/11/05/"}
