On Thu, Jul 16, 2026 at 2:04 PM Anlai Lu <[email protected]> wrote: > This series replaces per-character sputc() with bulk sputn() and > zero-copy put-area writes when formatting to ostreambuf_iterator. > Patch 2 adds a _Streambuf_sink that writes directly to a streambuf; > patch 3 specializes _Iter_sink to use it; patch 4 uses _Streambuf_sink > in std::print; patch 1 adds tests. > > Performance: > > fmt_to (ostreambuf_iter) 4 B 16 B 64 B 256 B 1024 B 4096 > B > v3: 0.97x 1.61x 4.15x 10.98x 51.97x 121.22x > > print (uses _Streambuf_sink, per [ostream.formatted.print]): > 4 B 16 B 64 B 256 B 1024 B > 4096 B > before (ns/op): 87 114 112 118 190 286 > after (ns/op): 78 78 77 86 82 134 > ratio: 1.12x 1.47x 1.46x 1.37x 2.33x 2.14x > > string / fmt_to_scanner unchanged (controls). > Hi, I have posted an updated version of first 3 patches (see my comments on fourth one, explaining why it is non-conforming, and needs a paper to allow taht optimization). Would you mind redoing your tests on the updated patches, as I do not have access to them?
> > Changes in v3 > ============== > (Items marked with [R] were suggested by Tomasz Kaminski.) > > - Extracted protected _M_flush(span) from _M_overflow so that both > _M_overflow and _M_finish share the same write path, and the > _Iter_sink specialization reuses it too. [R] > - Added _M_write_failed flag and _M_discarding() override to > _Streambuf_sink, so the format engine stops output early after a > write failure. [R] > - Changed _M_max from __diff_t to size_t with _S_no_limit sentinel. [R] > - Error propagation to the iterator moved to _Iter_sink::_M_finish > (checked just before returning std::move), so _M_overflow no longer > touches _M_out. > - Used _Streambuf_sink directly in vprint_nonunicode (patch 4), > replacing the _Str_sink + __ostream_write two-phase approach. > - Added _M_set_failed() to ostreambuf_iterator for the iterator's > error propagation path. > - Added try/catch in _M_flush to handle sputn I/O exceptions without > letting them propagate as vformat exceptions. Per > [ostream.formatted.print]/(4.2), vformat exceptions propagate without > badbit; I/O exceptions from sputn are not vformat exceptions and > are converted to _M_write_failed, then reported as badbit per (4.4). > - _M_finish() now ref-qualified (&&) on all sinks for consistency; > the print path uses std::move to call it. [R] > - Extracted _M_limit(span) helper in the _Iter_sink specialization for > shared counting/truncation between _M_overflow and _M_finish; > _M_finish now calls _M_flush directly, skipping unnecessary buffer > restoration. [R] > > Not applied > ============ > 1. Destructor flush [R] > Flush in destruction runs after the error check in print, so it > cannot help report I/O errors. And no other sink flushes in its > destructor. > > 2. _M_bump override [R] > The default advances _M_next within the current span; commit > happens via _M_flush. A comment explains this. > > Anlai Lu (4): > libstdc++: Add tests for format_to with ostreambuf_iterator > libstdc++: Add _Streambuf_sink for direct streambuf formatting > libstdc++: Specialize _Iter_sink for ostreambuf_iterator > libstdc++: Optimize std::print using _Streambuf_sink > > libstdc++-v3/include/bits/ostream_print.h | 12 +- > .../include/bits/streambuf_iterator.h | 11 + > libstdc++-v3/include/std/format | 199 ++++++++++++++ > libstdc++-v3/include/std/streambuf | 4 + > .../format/functions/format_to_ostreambuf.cc | 244 ++++++++++++++++++ > 5 files changed, 463 insertions(+), 7 deletions(-) > create mode 100644 > libstdc++-v3/testsuite/std/format/functions/format_to_ostreambuf.cc > > base-commit: a0591d86bd2be838e30c132481a0b15a6ba1e112 > > -- > 2.34.1 > >
