On Fri, Jul 24, 2026 at 10:14 PM Tomasz Kaminski <[email protected]> wrote:
> > > On Fri, Jul 24, 2026 at 4:49 PM Tomasz Kaminski <[email protected]> > wrote: > >> >> >> On Fri, Jul 24, 2026 at 4:28 PM Anlai Lu <[email protected]> wrote: >> >>> When _Duration uses integer seconds precision (period::den == 1, >>> not floating-point), time_point formatting has no sub-second >>> digits and therefore no locale-dependent components. Add an >>> overload of __detail::__chrono_write that conditionally skips >>> the locale argument, eliminating the format_to_n locale overload >>> and the basic_format_context locale member initialization. >>> >>> libstdc++-v3/ChangeLog: >>> >>> * include/bits/chrono_io.h >>> (__detail::__chrono_write): New overload skipping locale >>> for integer-second durations. >>> (operator<< for hh_mm_ss, sys_time, utc_time, tai_time, >>> gps_time, file_time, local_time, zoned_time): Use it. >>> >>> Suggested-by: Tomasz Kamiński <[email protected]> >>> Signed-off-by: Anlai Lu <[email protected]> >>> --- >>> >>> Performance data (Xeon, -O2, core pinned, turbo off, 50M iters): >>> >>> den==1 types instructions branches >>> --------------- ------------ -------- >>> sys_time_s -4.2% -9.6% >>> utc_time_s -3.8% -8.7% >>> tai_time_s -4.2% -9.6% >>> gps_time_s -4.2% -9.6% >>> file_time_s -4.2% -9.6% >>> local_time_s -4.2% -9.6% >>> zoned_time_s -2.4% -4.6% >>> hh_mm_ss_s -6.0% -13.6% >>> >>> den!=1 (control): >>> sys_time_ms +1.0% +1.1% >>> utc_time_ms +1.0% +1.1% >>> local_time_ms +1.0% +1.1% >>> hh_mm_ss_ms +1.3% +1.4% >>> >>> The den==1 improvement comes from compile-time removal of the >>> format_to_n locale overload and basic_format_context locale >>> member. The den!=1 +1.0% is a known measurement artifact from >>> mixing 12 time_point instantiations in one translation unit; >>> isolated single-type tests show zero difference. >>> >>> >>> libstdc++-v3/include/bits/chrono_io.h | 35 +++++++++++++++++++-------- >>> 1 file changed, 25 insertions(+), 10 deletions(-) >>> >>> diff --git a/libstdc++-v3/include/bits/chrono_io.h >>> b/libstdc++-v3/include/bits/chrono_io.h >>> index c5170368f..fa40290a8 100644 >>> --- a/libstdc++-v3/include/bits/chrono_io.h >>> +++ b/libstdc++-v3/include/bits/chrono_io.h >>> @@ -3635,6 +3635,21 @@ namespace __detail >>> return std::__ostream_insert(__os, __s.data(), __s.size()); >>> } >>> >>> + // Overload of __chrono_write that conditionally skips locale for >>> + // integer-second time_points, hh_mm_ss, and zoned_time. >>> + template<size_t _BufSize, typename _Duration, typename _CharT, >>> + typename _Traits, typename _Tp> >>> + inline basic_ostream<_CharT, _Traits>& >>> >> Mark above with always_inline. >> >>> + __chrono_write(basic_ostream<_CharT, _Traits>& __os, >>> + const _Tp& __val) >>> >> I would preffer having a different name, for this, maybe like >> chrono_write_time, >> Rename _Tp to _TimePoint and put: >> using _Duration = typename _TimePoint::duration in function. >> > Looking at the hh_mm_ss::fractional_widht, I have realized that we are > computing > the number of subsecond digits printed in terms of the period, ignoring > the rep (if floating point), > so the formatter does not need to look at rep, only at period. > A quick test also confirms this is the case for all implementations: > https://godbolt.org/z/18s61qeec > However, I checked above with date, and it prints 6 digits. Let's keep them localized (as before) for floating_point_rep. I.e. ignore the suggestion from this e-mail and keep using (!treat_as_floating_point_v<typename _Duration::rep> && _Duration::period::den == 1) condition. > So we could check only _Duration::period::den == 1 inside if constexpr, > even > for floating-point types. > > However, we will also need to correct the default configuration for the > formatter in this > case, in the __formatter_duration::_S_spec_for we have following condition: > if ((__parts & _TimeOfDay) != 0) > __res._M_localized = __res._M_prec > 0 || > __res._M_floating_point_rep; > This again should look only at den, so become: > __res._M_localized = __res._M_prec > 0; > > Could you also handle that in the revision? > > >> >>> + { >>> + if constexpr (!treat_as_floating_point_v<typename _Duration::rep> >>> + && _Duration::period::den == 1) >>> + return __chrono_write<_BufSize>(__os, __val); >>> + else >>> + return __chrono_write<_BufSize>(__os, __val, __os.getloc()); >>> + } >>> + >>> } // namespace __detail >>> /// @endcond >>> >>> @@ -3740,7 +3755,7 @@ namespace __detail >>> operator<<(basic_ostream<_CharT, _Traits>& __os, >>> const weekday_last& __wdl) >>> { return __detail::__chrono_write<128>(__os, __wdl, __os.getloc()); >>> } >>> - >>> + >>> template<typename _CharT, typename _Traits> >>> inline basic_ostream<_CharT, _Traits>& >>> operator<<(basic_ostream<_CharT, _Traits>& __os, const month_day& >>> __md) >>> @@ -3846,7 +3861,7 @@ namespace __detail >>> inline basic_ostream<_CharT, _Traits>& >>> operator<<(basic_ostream<_CharT, _Traits>& __os, >>> const hh_mm_ss<_Duration>& __hms) >>> - { return __detail::__chrono_write<64>(__os, __hms, __os.getloc()); } >>> + { return __detail::__chrono_write<64, _Duration>(__os, __hms); } >>> >> And then repeat the `if constexpr` branches here. >> > You could use check for fractional_width member in this case, and see if > it's greater than zero > >> >> >>> >>> #if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI >>> /// Writes a sys_info object to an ostream in an unspecified format. >>> @@ -3866,7 +3881,7 @@ namespace __detail >>> inline basic_ostream<_CharT, _Traits>& >>> operator<<(basic_ostream<_CharT, _Traits>& __os, >>> const zoned_time<_Duration, _TimeZonePtr>& __t) >>> - { return __detail::__chrono_write<128>(__os, __t, __os.getloc()); } >>> + { return __detail::__chrono_write<128, _Duration>(__os, __t); } >>> >> It would be less suprising to use zoned_type ::duration here (which is >> common type >> with seconds). My proposed chrono_write_time will achieve it. >> >>> #endif >>> >>> template<typename _CharT, typename _Traits, typename _Duration> >>> @@ -3875,12 +3890,12 @@ namespace __detail >>> inline basic_ostream<_CharT, _Traits>& >>> operator<<(basic_ostream<_CharT, _Traits>& __os, >>> const sys_time<_Duration>& __tp) >>> - { return __detail::__chrono_write<64>(__os, __tp, __os.getloc()); } >>> + { return __detail::__chrono_write<64, _Duration>(__os, __tp); } >>> >>> template<typename _CharT, typename _Traits> >>> inline basic_ostream<_CharT, _Traits>& >>> operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_days& >>> __dp) >>> - { return __detail::__chrono_write<32>(__os, __dp); }; >>> + { return __detail::__chrono_write<32>(__os, __dp); } >>> >>> template<typename _CharT, typename _Traits, typename _Duration, >>> typename _Alloc = allocator<_CharT>> >>> @@ -3914,7 +3929,7 @@ namespace __detail >>> inline basic_ostream<_CharT, _Traits>& >>> operator<<(basic_ostream<_CharT, _Traits>& __os, >>> const utc_time<_Duration>& __t) >>> - { return __detail::__chrono_write<64>(__os, __t, __os.getloc()); } >>> + { return __detail::__chrono_write<64, _Duration>(__os, __t); } >>> >>> template<typename _CharT, typename _Traits, typename _Duration, >>> typename _Alloc = allocator<_CharT>> >>> @@ -3946,7 +3961,7 @@ namespace __detail >>> inline basic_ostream<_CharT, _Traits>& >>> operator<<(basic_ostream<_CharT, _Traits>& __os, >>> const tai_time<_Duration>& __t) >>> - { return __detail::__chrono_write<64>(__os, __t, __os.getloc()); } >>> + { return __detail::__chrono_write<64, _Duration>(__os, __t); } >>> >>> template<typename _CharT, typename _Traits, typename _Duration, >>> typename _Alloc = allocator<_CharT>> >>> @@ -3982,7 +3997,7 @@ namespace __detail >>> inline basic_ostream<_CharT, _Traits>& >>> operator<<(basic_ostream<_CharT, _Traits>& __os, >>> const gps_time<_Duration>& __t) >>> - { return __detail::__chrono_write<64>(__os, __t, __os.getloc()); } >>> + { return __detail::__chrono_write<64, _Duration>(__os, __t); } >>> >>> template<typename _CharT, typename _Traits, typename _Duration, >>> typename _Alloc = allocator<_CharT>> >>> @@ -4017,7 +4032,7 @@ namespace __detail >>> inline basic_ostream<_CharT, _Traits>& >>> operator<<(basic_ostream<_CharT, _Traits>& __os, >>> const file_time<_Duration>& __t) >>> - { return __detail::__chrono_write<64>(__os, __t, __os.getloc()); } >>> + { return __detail::__chrono_write<64, _Duration>(__os, __t); } >>> >>> template<typename _CharT, typename _Traits, typename _Duration, >>> typename _Alloc = allocator<_CharT>> >>> @@ -4040,7 +4055,7 @@ namespace __detail >>> // _GLIBCXX_RESOLVE_LIB_DEFECTS >>> // 4257. Stream insertion for chrono::local_time should be >>> constrained >>> requires requires(const sys_time<_Duration>& __st) { __os << __st; } >>> - { return __detail::__chrono_write<64>(__os, __lt, __os.getloc()); } >>> + { return __detail::__chrono_write<64, _Duration>(__os, __lt); } >>> >>> template<typename _CharT, typename _Traits, typename _Duration, >>> typename _Alloc = allocator<_CharT>> >>> -- >>> 2.34.1 >>> >>>
