On Fri, 24 Jul 2026 at 15:50, 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,
Yes please, I was also going to ask for the new function to have a
different name.
> Rename _Tp to _TimePoint and put:
> using _Duration = typename _TimePoint::duration in function.
>
>>
>> + {
>> + 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.
>
>>
>>
>> #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
>>