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

Reply via email to