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

Reply via email to