]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Use std::from_chars to speed up parsing subsecond durations
authorJonathan Wakely <jwakely@redhat.com>
Thu, 7 Mar 2024 13:47:46 +0000 (13:47 +0000)
committerJonathan Wakely <jwakely@redhat.com>
Thu, 7 Mar 2024 23:50:39 +0000 (23:50 +0000)
With std::from_chars we can parse subsecond durations much faster than
with std::num_get, as shown in the microbenchmarks below. We were using
std::num_get and std::numpunct in order to parse a number with the
locale's decimal point character. But we copy the chars from the input
stream into a new buffer anyway, so we can replace the locale's decimal
point with '.' in that buffer, and then we can use std::from_chars on
it.

Benchmark                Time             CPU   Iterations
----------------------------------------------------------
from_chars_millisec    158 ns          158 ns      4524046
num_get_millisec       192 ns          192 ns      3644626
from_chars_microsec    164 ns          163 ns      4330627
num_get_microsec       205 ns          205 ns      3413452
from_chars_nanosec     173 ns          173 ns      4072653
num_get_nanosec        227 ns          227 ns      3105161

libstdc++-v3/ChangeLog:

* include/bits/chrono_io.h (_Parser::operator()): Use
std::from_chars to parse fractional seconds.

libstdc++-v3/include/bits/chrono_io.h

index b8f0657bee9a11ab256e1f526380e0532aa4a265..412e8b83fb729c8fc410bf762561c050907974ab 100644 (file)
@@ -37,6 +37,7 @@
 #include <sstream> // ostringstream
 #include <iomanip> // setw, setfill
 #include <format>
+#include <charconv> // from_chars
 
 #include <bits/streambuf_iterator.h>
 
@@ -3597,13 +3598,17 @@ namespace __detail
                        __err |= ios_base::eofbit;
                      else
                        {
-                         auto& __np = use_facet<numpunct<_CharT>>(__loc);
-                         auto __dp = __np.decimal_point();
+                         _CharT __dp = '.';
+                         if (__loc != locale::classic())
+                           {
+                             auto& __np = use_facet<numpunct<_CharT>>(__loc);
+                             __dp = __np.decimal_point();
+                           }
                          _CharT __c = _Traits::to_char_type(__i);
                          if (__c == __dp)
                            {
                              (void) __is.get();
-                             __buf.put(__c);
+                             __buf.put('.');
                              int __prec
                                = hh_mm_ss<_Duration>::fractional_width;
                              do
@@ -3618,14 +3623,17 @@ namespace __detail
                            }
                        }
 
-                     if (!__is_failed(__err))
+                     if (!__is_failed(__err)) [[likely]]
                        {
-                         auto& __ng = use_facet<num_get<_CharT>>(__loc);
-                         long double __val;
-                         ios_base::iostate __err2{};
-                         __ng.get(__buf, {}, __buf, __err2, __val);
-                         if (__is_failed(__err2)) [[unlikely]]
-                           __err |= __err2;
+                         long double __val{};
+                         string __str = std::move(__buf).str();
+                         auto __first = __str.data();
+                         auto __last = __first + __str.size();
+                         using enum chars_format;
+                         auto [ptr, ec] = std::from_chars(__first, __last,
+                                                          __val, fixed);
+                         if ((bool)ec || ptr != __last) [[unlikely]]
+                           __err |= ios_base::failbit;
                          else
                            {
                              duration<long double> __fs(__val);