Re: [PATCH v2] libstdc++: Skip locale in chrono operator<< for integer seconds precision

Tomasz Kaminski <[email protected]> Tue, 4 Aug 2026 08:33:59 +0200
Newsgroups gmane.comp.gcc.patches
Message-ID <CAKvuMXBqCDeq4OngaH1xAJJCyV=jLmoeU5_PYXYAkNHQyh39pA@mail.gmail.com>
Hi Anlai,

Patch was merged, thanks again for doing it.

Regards,
Tomasz

On Mon, Aug 3, 2026 at 6:17 PM Jonathan Wakely <[email protected]> wrote:

>
>
> On Mon, 27 Jul 2026 at 08:41, Tomasz Kaminski <[email protected]> wrote:
>
>> Thanks this looks good to me. I will handle merging it once approved,
>> with adjustments to the changelog listed below:
>>
>
> OK with those changes, thanks.
>


>
>
>
>>
>> On Mon, Jul 27, 2026 at 5:29 AM 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
>>> __detail::__chrono_write_time that conditionally skips the locale
>>> argument for integer-second time_points, eliminating the
>>> format_to_n locale overload and the basic_format_context
>>> locale member initialization.
>>>
>>> Also add [[__gnu__::__always_inline__]] to
>>> __formatter_chrono::_M_subsecs to mitigate a small mixed-TU
>>> inlining regression.
>>>
>>> libstdc++-v3/ChangeLog:
>>>
>>>         * include/bits/chrono_io.h
>>>         (__detail::__chrono_write_time): New function that conditionally
>>>         skips locale for integer-second time_points.
>>>         (__formatter_chrono::_M_subsecs): Add __always_inline__.
>>>         (operator<< for hh_mm_ss): Use if constexpr to skip locale
>>>         for integer-second durations.
>>>         (operator<< for sys_time, utc_time, tai_time, gps_time,
>>>         file_time, local_time, zoned_time): Use __chrono_write_time.
>>>
>> Changed it to:
>>             * include/bits/chrono_io.h (__detail::__chrono_write_time):
>>             New function that conditionally skips locale for
>> integer-second
>>             time_points.
>>             (__formatter_chrono::_M_subsecs): Add __always_inline__.
>>             (operator<<(basic_ostream<...>, const hh_mm_ss<_Duration>&)):
>>             Use if constexpr to skip locale for integer-second durations.
>>             (operator<<(basic_ostream<...>, const zoned_time<_Duration>&))
>>             (operator<<(basic_ostream<...>, const sys_time<_Duration>&))
>>             (operator<<(basic_ostream<...>, const utc_time<_Duration>&))
>>             (operator<<(basic_ostream<...>, const tai_time<_Duration>&))
>>             (operator<<(basic_ostream<...>, const file_time<_Duration>&))
>>             (operator<<(basic_ostream<...>, const
>> local_time<_Duration>&)):
>>             Use __chrono_write_time.
>>             (operator<<(basic_ostream<...>, const sys_days<_Duration>&)):
>>             Remove trailing semicolon.
>>
>>
>>> Suggested-by: Tomasz Kamiński <[email protected]>
>>>
>> Changed the above to:
>>  Reviewed-by: Tomasz Kamiński <[email protected]>
>>
>>> Signed-off-by: Anlai Lu <[email protected]>
>>> ---
>>> Performance data (Xeon, -O2, core pinned, turbo off):
>>>
>>>   den==1 types     instructions   branches
>>>   ---------------  ------------   --------
>>>   sys_time_s         -5.0%        -11.3%
>>>   utc_time_s         -5.0%        -11.3%
>>>   tai_time_s         -5.0%        -11.3%
>>>   gps_time_s         -5.0%        -11.3%
>>>   file_time_s        -5.0%        -11.3%
>>>   local_time_s       -5.1%        -11.3%
>>>   zoned_time_s       -2.8%         -5.3%
>>>   hh_mm_ss_s         -7.1%        -15.7%
>>>
>>>   den!=1:
>>>   sys_time_ms        +0.3%         +0.6%
>>>   utc_time_ms        +0.3%         +0.6%
>>>   local_time_ms      +0.3%         +0.6%
>>>   hh_mm_ss_ms        +0.4%         +0.7%
>>>
>>> ---
>>>  libstdc++-v3/include/bits/chrono_io.h | 44 +++++++++++++++++++++------
>>>  1 file changed, 34 insertions(+), 10 deletions(-)
>>>
>>> diff --git a/libstdc++-v3/include/bits/chrono_io.h
>>> b/libstdc++-v3/include/bits/chrono_io.h
>>> index c5170368f..2cbd61ccd 100644
>>> --- a/libstdc++-v3/include/bits/chrono_io.h
>>> +++ b/libstdc++-v3/include/bits/chrono_io.h
>>> @@ -1656,6 +1656,7 @@ namespace __format
>>>         }
>>>
>>>        template<typename _OutIter, typename _FormatContext>
>>> +       [[__gnu__::__always_inline__]]
>>>         _OutIter
>>>         _M_subsecs(const _ChronoData<_CharT>& __t, _OutIter __out,
>>>                    _FormatContext& __ctx) const
>>> @@ -3635,6 +3636,23 @@ namespace __detail
>>>        return std::__ostream_insert(__os, __s.data(), __s.size());
>>>      }
>>>
>>> +  // Wrapper around __chrono_write that skips locale for
>>> +  // integer-second time_points.
>>> +  template<size_t _BufSize, typename _TimePoint, typename _CharT,
>>> +          typename _Traits>
>>> +    [[__gnu__::__always_inline__]]
>>> +    inline basic_ostream<_CharT, _Traits>&
>>> +    __chrono_write_time(basic_ostream<_CharT, _Traits>& __os,
>>> +                       const _TimePoint& __tp)
>>> +    {
>>> +      using _Duration = typename _TimePoint::duration;
>>> +      if constexpr (!treat_as_floating_point_v<typename _Duration::rep>
>>> +                   && _Duration::period::den == 1)
>>> +       return __chrono_write<_BufSize>(__os, __tp);
>>> +      else
>>> +       return __chrono_write<_BufSize>(__os, __tp, __os.getloc());
>>> +    }
>>> +
>>>  } // namespace __detail
>>>  /// @endcond
>>>
>>> @@ -3740,7 +3758,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 +3864,13 @@ 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()); }
>>> +    {
>>> +      if constexpr (!treat_as_floating_point_v<typename _Duration::rep>
>>> +                   && _Duration::period::den == 1)
>>> +       return __detail::__chrono_write<64>(__os, __hms);
>>> +      else
>>> +       return __detail::__chrono_write<64>(__os, __hms, __os.getloc());
>>> +    }
>>>
>>>  #if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
>>>    /// Writes a sys_info object to an ostream in an unspecified format.
>>> @@ -3866,7 +3890,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_time<128>(__os, __t); }
>>>  #endif
>>>
>>>    template<typename _CharT, typename _Traits, typename _Duration>
>>> @@ -3875,12 +3899,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_time<64>(__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 +3938,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_time<64>(__os, __t); }
>>>
>>>    template<typename _CharT, typename _Traits, typename _Duration,
>>>            typename _Alloc = allocator<_CharT>>
>>> @@ -3946,7 +3970,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_time<64>(__os, __t); }
>>>
>>>    template<typename _CharT, typename _Traits, typename _Duration,
>>>            typename _Alloc = allocator<_CharT>>
>>> @@ -3982,7 +4006,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_time<64>(__os, __t); }
>>>
>>>    template<typename _CharT, typename _Traits, typename _Duration,
>>>            typename _Alloc = allocator<_CharT>>
>>> @@ -4017,7 +4041,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_time<64>(__os, __t); }
>>>
>>>    template<typename _CharT, typename _Traits, typename _Duration,
>>>            typename _Alloc = allocator<_CharT>>
>>> @@ -4040,7 +4064,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_time<64>(__os, __lt); }
>>>
>>>    template<typename _CharT, typename _Traits, typename _Duration,
>>>            typename _Alloc = allocator<_CharT>>
>>> --
>>> 2.34.1
>>>
>>>