This is v2 of the patch series to eliminate temporary std::string
allocations in chrono operator<< overloads.
Changes from v1:
- Dropped the format_to/ostreambuf_iterator approach and the
_Iter_sink<ostreambuf_iterator> specialization. The format_to
approach bypasses the stream sentry mechanism and loses width/fill
alignment — since operator<< is required to behave as a formatted
output function, this is a correctness issue. Jonathan Wakely noted
that v1 also incorrectly used badbit (should be failbit) and that
error state on a temporary stream was not propagated to the caller.
Tomasz Kaminski noted that the _Iter_sink approach had
exception-safety concerns and unclear API boundaries on
ostreambuf_iterator.
- New approach: __detail::__chrono_write formats into a 128-byte
stack buffer via __do_vformat_to_n and writes through
__ostream_insert, which correctly handles sentry construction and
width/fill padding. Error propagation and failbit semantics are
handled by the standard sentry mechanism — no manual state
management needed. Exception safety is preserved because all
formatting completes into the stack buffer before any characters
are written to the stream, so there is no risk of partial output.
If output exceeds the buffer, it falls back to std::vformat with
dynamic allocation.
- __detail::__empty_fmt bridges _S_empty_fs() through
basic_format_string to get a basic_string_view. Types whose
formatters already handle empty spec (day, month, year, weekday,
year_month_day) use this to skip format-string validation and
manual ok() checks. This follows Tomasz's observation that
_S_empty_fs() can replace hand-rolled format strings for these
types.
- weekday_indexed and weekday_last now use __chrono_write directly,
per [time.cal.wdidx.nonmembers] and [time.cal.wdlast.nonmembers].
The standard defines these operator<< as equivalent to
os << format(os.getloc(), "...", wdi.weekday(), ...)
so no special failbit handling is needed. This eliminates the
temporary basic_stringstream allocations for these types.
- Patch 1 (tests) verifies formatted output function semantics:
sentry construction (badbit check, tied stream flush), width/fill
with all three alignment modes, width reset, locale-aware output,
weekday_indexed content, and wchar_t smoke test.
Performance (x86_64, GCC 17, -O2):
Chrono operator<< latency:
year_month_day 266 -> 203 ns (1.31x)
weekday 303 -> 251 ns (1.20x)
sys_info 1162 -> 986 ns (1.18x)
day 252 -> 217 ns (1.16x)
sys_time 437 -> 410 ns (1.07x)
zoned_time 591 -> 571 ns (1.03x)
hh_mm_ss 268 -> 261 ns (1.03x)
Multi-threaded throughput (sys_time): 1.07x-1.11x across 1-8 threads.
Instruction count reduction: -2.8% to -22.8% across all types.
No regressions in IPC, cache misses, or branch prediction.
Tested on x86_64-linux-gnu. libstdc++ testsuite (std/time/*) clean.
Smoke test: all chrono operator<< outputs verified byte-identical.
Anlai Lu (2):
libstdc++: Add stream state tests for chrono operator<<
libstdc++: Use __chrono_write and __empty_fmt for chrono ostream
insertion
libstdc++-v3/include/bits/chrono_io.h | 202 ++++++++++--------
.../testsuite/std/time/ostream_insert.cc | 163 ++++++++++++++
2 files changed, 278 insertions(+), 87 deletions(-)
create mode 100644 libstdc++-v3/testsuite/std/time/ostream_insert.cc
--
2.34.1