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

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

Reply via email to