[libcxx-commits] [libcxx] 6c11aeb - [libc++] Improve std::to_chars for base != 10.

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Sat May 14 04:01:40 PDT 2022


Author: Mark de Wever
Date: 2022-05-14T13:01:35+02:00
New Revision: 6c11aebd302dee06d734e6025b4399cd12f204c4

URL: https://github.com/llvm/llvm-project/commit/6c11aebd302dee06d734e6025b4399cd12f204c4
DIFF: https://github.com/llvm/llvm-project/commit/6c11aebd302dee06d734e6025b4399cd12f204c4.diff

LOG: [libc++] Improve std::to_chars for base != 10.

This improves the speed of `to_chars` for bases 2, 8, and 16.
These bases are common and used in `<format>`. This change
uses a lookup table, like done in base 10 and causes an increase
in code size. The change has a small overhead for the other bases.

```
Benchmark                             Time             CPU      Time Old      Time New       CPU Old       CPU New
------------------------------------------------------------------------------------------------------------------
BM_to_chars_good/2                 -0.9476         -0.9476           252            13           252            13
BM_to_chars_good/3                 +0.0018         +0.0018           145           145           145           145
BM_to_chars_good/4                 +0.0108         +0.0108           104           105           104           105
BM_to_chars_good/5                 +0.0159         +0.0160            89            91            89            91
BM_to_chars_good/6                 +0.0162         +0.0162            80            81            80            81
BM_to_chars_good/7                 +0.0117         +0.0117            72            73            72            73
BM_to_chars_good/8                 -0.8643         -0.8643            64             9            64             9
BM_to_chars_good/9                 +0.0095         +0.0095            60            60            60            60
BM_to_chars_good/10                +0.0540         +0.0540             6             6             6             6
BM_to_chars_good/11                +0.0299         +0.0299            55            57            55            57
BM_to_chars_good/12                +0.0060         +0.0060            48            49            49            49
BM_to_chars_good/13                +0.0102         +0.0102            48            48            48            48
BM_to_chars_good/14                +0.0184         +0.0185            47            48            47            48
BM_to_chars_good/15                +0.0269         +0.0269            44            45            44            45
BM_to_chars_good/16                -0.8207         -0.8207            37             7            37             7
BM_to_chars_good/17                +0.0241         +0.0241            37            38            37            38
BM_to_chars_good/18                +0.0221         +0.0221            37            38            37            38
BM_to_chars_good/19                +0.0222         +0.0223            37            38            37            38
BM_to_chars_good/20                +0.0317         +0.0317            38            39            38            39
BM_to_chars_good/21                +0.0342         +0.0341            38            39            38            39
BM_to_chars_good/22                +0.0336         +0.0336            36            38            36            38
BM_to_chars_good/23                +0.0222         +0.0222            34            35            34            35
BM_to_chars_good/24                +0.0185         +0.0185            31            32            31            32
BM_to_chars_good/25                +0.0157         +0.0157            32            32            32            32
BM_to_chars_good/26                +0.0181         +0.0181            32            32            32            32
BM_to_chars_good/27                +0.0153         +0.0153            32            32            32            32
BM_to_chars_good/28                +0.0179         +0.0179            32            32            32            32
BM_to_chars_good/29                +0.0189         +0.0189            32            33            32            33
BM_to_chars_good/30                +0.0212         +0.0212            32            33            32            33
BM_to_chars_good/31                +0.0221         +0.0221            32            33            32            33
BM_to_chars_good/32                +0.0292         +0.0292            32            33            32            33
BM_to_chars_good/33                +0.0319         +0.0319            32            33            32            33
BM_to_chars_good/34                +0.0411         +0.0410            33            34            33            34
BM_to_chars_good/35                +0.0515         +0.0515            33            34            33            34
BM_to_chars_good/36                +0.0502         +0.0502            32            34            32            34
BM_to_chars_bad/2                  -0.8752         -0.8752            40             5            40             5
BM_to_chars_bad/3                  +0.1952         +0.1952            21            26            21            26
BM_to_chars_bad/4                  +0.3626         +0.3626            16            22            16            22
BM_to_chars_bad/5                  +0.2267         +0.2268            17            21            17            21
BM_to_chars_bad/6                  +0.3560         +0.3559            14            19            14            19
BM_to_chars_bad/7                  +0.4599         +0.4600            12            18            12            18
BM_to_chars_bad/8                  -0.5074         -0.5074            11             5            11             5
BM_to_chars_bad/9                  +0.4814         +0.4814            10            15            10            15
BM_to_chars_bad/10                 +0.7761         +0.7761             2             4             2             4
BM_to_chars_bad/11                 +0.3948         +0.3948            12            16            12            16
BM_to_chars_bad/12                 +0.3203         +0.3203            10            13            10            13
BM_to_chars_bad/13                 +0.3067         +0.3067            11            14            11            14
BM_to_chars_bad/14                 +0.2235         +0.2235            12            14            12            14
BM_to_chars_bad/15                 +0.2675         +0.2675            11            14            11            14
BM_to_chars_bad/16                 -0.1801         -0.1801             7             5             7             5
BM_to_chars_bad/17                 +0.5651         +0.5651             7            11             7            11
BM_to_chars_bad/18                 +0.5407         +0.5406             7            11             7            11
BM_to_chars_bad/19                 +0.5593         +0.5593             8            12             8            12
BM_to_chars_bad/20                 +0.5823         +0.5823             8            13             8            13
BM_to_chars_bad/21                 +0.6032         +0.6032             9            15             9            15
BM_to_chars_bad/22                 +0.6407         +0.6408             9            14             9            14
BM_to_chars_bad/23                 +0.6292         +0.6292             7            12             7            12
BM_to_chars_bad/24                 +0.5784         +0.5784             6            10             6            10
BM_to_chars_bad/25                 +0.5784         +0.5784             6            10             6            10
BM_to_chars_bad/26                 +0.5713         +0.5713             7            10             7            10
BM_to_chars_bad/27                 +0.5969         +0.5969             7            11             7            11
BM_to_chars_bad/28                 +0.6131         +0.6131             7            11             7            11
BM_to_chars_bad/29                 +0.6937         +0.6937             7            11             7            11
BM_to_chars_bad/30                 +0.7655         +0.7656             7            12             7            12
BM_to_chars_bad/31                 +0.8939         +0.8939             6            12             6            12
BM_to_chars_bad/32                 +1.0157         +1.0157             6            13             6            13
BM_to_chars_bad/33                 +1.0279         +1.0279             7            14             7            14
BM_to_chars_bad/34                 +1.0388         +1.0388             7            14             7            14
BM_to_chars_bad/35                 +1.0990         +1.0990             7            15             7            15
BM_to_chars_bad/36                 +1.1503         +1.1503             7            15             7            15
```

Reviewed By: ldionne, #libc

Differential Revision: https://reviews.llvm.org/D97705

Added: 
    

Modified: 
    libcxx/include/charconv

Removed: 
    


################################################################################
diff  --git a/libcxx/include/charconv b/libcxx/include/charconv
index ca717dc4a0463..d5e485a367513 100644
--- a/libcxx/include/charconv
+++ b/libcxx/include/charconv
@@ -319,6 +319,197 @@ __to_chars_integral(char* __first, char* __last, _Tp __value, int __base,
     return __to_chars_integral(__first, __last, __x, __base, false_type());
 }
 
+namespace __itoa {
+
+static constexpr char __base_2_lut[64] = {
+  '0', '0', '0', '0',
+  '0', '0', '0', '1',
+  '0', '0', '1', '0',
+  '0', '0', '1', '1',
+  '0', '1', '0', '0',
+  '0', '1', '0', '1',
+  '0', '1', '1', '0',
+  '0', '1', '1', '1',
+  '1', '0', '0', '0',
+  '1', '0', '0', '1',
+  '1', '0', '1', '0',
+  '1', '0', '1', '1',
+  '1', '1', '0', '0',
+  '1', '1', '0', '1',
+  '1', '1', '1', '0',
+  '1', '1', '1', '1'
+};
+
+static constexpr char __base_8_lut[128] = {
+  '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7',
+  '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6', '1', '7',
+  '2', '0', '2', '1', '2', '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7',
+  '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', '3', '5', '3', '6', '3', '7',
+  '4', '0', '4', '1', '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7',
+  '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7',
+  '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7',
+  '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7'
+};
+
+static constexpr char __base_16_lut[512] = {
+  '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7', '0', '8', '0', '9', '0', 'a', '0', 'b', '0', 'c', '0', 'd', '0', 'e', '0', 'f',
+  '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '1', 'a', '1', 'b', '1', 'c', '1', 'd', '1', 'e', '1', 'f',
+  '2', '0', '2', '1', '2', '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', '2', 'a', '2', 'b', '2', 'c', '2', 'd', '2', 'e', '2', 'f',
+  '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '3', 'a', '3', 'b', '3', 'c', '3', 'd', '3', 'e', '3', 'f',
+  '4', '0', '4', '1', '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', '4', 'a', '4', 'b', '4', 'c', '4', 'd', '4', 'e', '4', 'f',
+  '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9', '5', 'a', '5', 'b', '5', 'c', '5', 'd', '5', 'e', '5', 'f',
+  '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', '6', '9', '6', 'a', '6', 'b', '6', 'c', '6', 'd', '6', 'e', '6', 'f',
+  '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7', '7', '8', '7', '9', '7', 'a', '7', 'b', '7', 'c', '7', 'd', '7', 'e', '7', 'f',
+  '8', '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', '8', 'a', '8', 'b', '8', 'c', '8', 'd', '8', 'e', '8', 'f',
+  '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', '7', '9', '8', '9', '9', '9', 'a', '9', 'b', '9', 'c', '9', 'd', '9', 'e', '9', 'f',
+  'a', '0', 'a', '1', 'a', '2', 'a', '3', 'a', '4', 'a', '5', 'a', '6', 'a', '7', 'a', '8', 'a', '9', 'a', 'a', 'a', 'b', 'a', 'c', 'a', 'd', 'a', 'e', 'a', 'f',
+  'b', '0', 'b', '1', 'b', '2', 'b', '3', 'b', '4', 'b', '5', 'b', '6', 'b', '7', 'b', '8', 'b', '9', 'b', 'a', 'b', 'b', 'b', 'c', 'b', 'd', 'b', 'e', 'b', 'f',
+  'c', '0', 'c', '1', 'c', '2', 'c', '3', 'c', '4', 'c', '5', 'c', '6', 'c', '7', 'c', '8', 'c', '9', 'c', 'a', 'c', 'b', 'c', 'c', 'c', 'd', 'c', 'e', 'c', 'f',
+  'd', '0', 'd', '1', 'd', '2', 'd', '3', 'd', '4', 'd', '5', 'd', '6', 'd', '7', 'd', '8', 'd', '9', 'd', 'a', 'd', 'b', 'd', 'c', 'd', 'd', 'd', 'e', 'd', 'f',
+  'e', '0', 'e', '1', 'e', '2', 'e', '3', 'e', '4', 'e', '5', 'e', '6', 'e', '7', 'e', '8', 'e', '9', 'e', 'a', 'e', 'b', 'e', 'c', 'e', 'd', 'e', 'e', 'e', 'f',
+  'f', '0', 'f', '1', 'f', '2', 'f', '3', 'f', '4', 'f', '5', 'f', '6', 'f', '7', 'f', '8', 'f', '9', 'f', 'a', 'f', 'b', 'f', 'c', 'f', 'd', 'f', 'e', 'f', 'f'
+};
+
+template <unsigned _Base>
+struct _LIBCPP_HIDDEN __integral;
+
+template <>
+struct _LIBCPP_HIDDEN __integral<2> {
+  template <typename _Tp>
+  _LIBCPP_HIDE_FROM_ABI static constexpr int __width(_Tp __value) noexcept {
+    // If value == 0 still need one digit. If the value != this has no
+    // effect since the code scans for the most significant bit set. (Note
+    // that __libcpp_clz doesn't work for 0.)
+    return numeric_limits<_Tp>::digits - std::__libcpp_clz(__value | 1);
+  }
+
+  template <typename _Tp>
+  _LIBCPP_HIDE_FROM_ABI static to_chars_result __to_chars(char* __first, char* __last, _Tp __value) {
+    ptr
diff _t __cap = __last - __first;
+    int __n = __width(__value);
+    if (__n > __cap)
+      return {__last, errc::value_too_large};
+
+    __last = __first + __n;
+    char* __p = __last;
+    const unsigned __divisor = 16;
+    while (__value > __divisor) {
+      unsigned __c = __value % __divisor;
+      __value /= __divisor;
+      __p -= 4;
+      std::memcpy(__p, &__base_2_lut[4 * __c], 4);
+    }
+    do {
+      unsigned __c = __value % 2;
+      __value /= 2;
+      *--__p = "01"[__c];
+    } while (__value != 0);
+    return {__last, errc(0)};
+  }
+};
+
+template <>
+struct _LIBCPP_HIDDEN __integral<8> {
+  template <typename _Tp>
+  _LIBCPP_HIDE_FROM_ABI static constexpr int __width(_Tp __value) noexcept {
+    // If value == 0 still need one digit. If the value != this has no
+    // effect since the code scans for the most significat bit set. (Note
+    // that __libcpp_clz doesn't work for 0.)
+    return ((numeric_limits<_Tp>::digits - std::__libcpp_clz(__value | 1)) + 2) / 3;
+  }
+
+  template <typename _Tp>
+  _LIBCPP_HIDE_FROM_ABI static to_chars_result __to_chars(char* __first, char* __last, _Tp __value) {
+    ptr
diff _t __cap = __last - __first;
+    int __n = __width(__value);
+    if (__n > __cap)
+      return {__last, errc::value_too_large};
+
+    __last = __first + __n;
+    char* __p = __last;
+    unsigned __divisor = 64;
+    while (__value > __divisor) {
+      unsigned __c = __value % __divisor;
+      __value /= __divisor;
+      __p -= 2;
+      std::memcpy(__p, &__base_8_lut[2 * __c], 2);
+    }
+    do {
+      unsigned __c = __value % 8;
+      __value /= 8;
+      *--__p = "01234567"[__c];
+    } while (__value != 0);
+    return {__last, errc(0)};
+  }
+
+};
+
+template <>
+struct _LIBCPP_HIDDEN __integral<16> {
+  template <typename _Tp>
+  _LIBCPP_HIDE_FROM_ABI static constexpr int __width(_Tp __value) noexcept {
+    // If value == 0 still need one digit. If the value != this has no
+    // effect since the code scans for the most significat bit set. (Note
+    // that __libcpp_clz doesn't work for 0.)
+    return (numeric_limits<_Tp>::digits - std::__libcpp_clz(__value | 1) + 3) / 4;
+  }
+
+  template <typename _Tp>
+  _LIBCPP_HIDE_FROM_ABI static to_chars_result __to_chars(char* __first, char* __last, _Tp __value) {
+    ptr
diff _t __cap = __last - __first;
+    int __n = __width(__value);
+    if (__n > __cap)
+      return {__last, errc::value_too_large};
+
+    __last = __first + __n;
+    char* __p = __last;
+    unsigned __divisor = 256;
+    while (__value > __divisor) {
+      unsigned __c = __value % __divisor;
+      __value /= __divisor;
+      __p -= 2;
+      std::memcpy(__p, &__base_16_lut[2 * __c], 2);
+    }
+    if (__first != __last)
+      do {
+        unsigned __c = __value % 16;
+        __value /= 16;
+        *--__p = "0123456789abcdef"[__c];
+      } while (__value != 0);
+    return {__last, errc(0)};
+  }
+};
+
+} // namespace __itoa
+
+template <unsigned _Base, typename _Tp,
+          typename enable_if<(sizeof(_Tp) >= sizeof(unsigned)), int>::type = 0>
+_LIBCPP_HIDE_FROM_ABI int
+__to_chars_integral_width(_Tp __value) {
+  return __itoa::__integral<_Base>::__width(__value);
+}
+
+template <unsigned _Base, typename _Tp,
+          typename enable_if<(sizeof(_Tp) < sizeof(unsigned)), int>::type = 0>
+_LIBCPP_HIDE_FROM_ABI int
+__to_chars_integral_width(_Tp __value) {
+  return std::__to_chars_integral_width<_Base>(static_cast<unsigned>(__value));
+}
+
+template <unsigned _Base, typename _Tp,
+          typename enable_if<(sizeof(_Tp) >= sizeof(unsigned)), int>::type = 0>
+_LIBCPP_HIDE_FROM_ABI to_chars_result
+__to_chars_integral(char* __first, char* __last, _Tp __value) {
+  return __itoa::__integral<_Base>::__to_chars(__first, __last, __value);
+}
+
+template <unsigned _Base, typename _Tp,
+          typename enable_if<(sizeof(_Tp) < sizeof(unsigned)), int>::type = 0>
+_LIBCPP_HIDE_FROM_ABI to_chars_result
+__to_chars_integral(char* __first, char* __last, _Tp __value) {
+  return std::__to_chars_integral<_Base>(__first, __last, static_cast<unsigned>(__value));
+}
+
 template <typename _Tp>
 _LIBCPP_AVAILABILITY_TO_CHARS _LIBCPP_HIDE_FROM_ABI int
 __to_chars_integral_width(_Tp __value, unsigned __base) {
@@ -352,9 +543,18 @@ inline _LIBCPP_HIDE_FROM_ABI to_chars_result
 __to_chars_integral(char* __first, char* __last, _Tp __value, int __base,
                     false_type)
 {
-  if (__base == 10)
+  if (__base == 10) [[likely]]
     return __to_chars_itoa(__first, __last, __value, false_type());
 
+  switch (__base) {
+  case 2:
+    return __to_chars_integral<2>(__first, __last, __value);
+  case 8:
+    return __to_chars_integral<8>(__first, __last, __value);
+  case 16:
+    return __to_chars_integral<16>(__first, __last, __value);
+  }
+
   ptr
diff _t __cap = __last - __first;
   int __n = __to_chars_integral_width(__value, __base);
   if (__n > __cap)


        


More information about the libcxx-commits mailing list