[libcxx-commits] [libcxx] [libc++] Optimize char_traits a bit (PR #72799)

via libcxx-commits libcxx-commits at lists.llvm.org
Sun Nov 19 08:58:08 PST 2023


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/72799

This implements two kinds of optimizations. Specifically
- `char_traits<char8_t>` uses `char` code paths; these are heavily
  optimized and the operations are equivalent
- `char16_t` and `char32_t` `find` uses `std::find` to forward to
  `wmemchr` if they have the same size


>From 7b104b23e510612ee1c10e5d0f9f95d15e27bed5 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sun, 19 Nov 2023 17:51:05 +0100
Subject: [PATCH] [libc++] Optimize char_traits a bit

This implements two kinds of optimizations. Specifically
- `char_traits<char8_t>` uses `char` code paths; these are heavily
  optimized and the operations are equivalent
- `char16_t` and `char32_t` `find` uses `std::find` to forward to
  `wmemchr` if they have the same size
---
 libcxx/include/__string/char_traits.h         | 118 +++++++-----------
 .../include/__string/constexpr_c_functions.h  |  13 +-
 2 files changed, 51 insertions(+), 80 deletions(-)

diff --git a/libcxx/include/__string/char_traits.h b/libcxx/include/__string/char_traits.h
index 17eade2f8188f76..b3bde455eb4a260 100644
--- a/libcxx/include/__string/char_traits.h
+++ b/libcxx/include/__string/char_traits.h
@@ -11,12 +11,14 @@
 
 #include <__algorithm/copy_n.h>
 #include <__algorithm/fill_n.h>
+#include <__algorithm/find.h>
 #include <__algorithm/find_end.h>
 #include <__algorithm/find_first_of.h>
 #include <__algorithm/min.h>
 #include <__compare/ordering.h>
 #include <__config>
 #include <__functional/hash.h>
+#include <__functional/identity.h>
 #include <__iterator/iterator_traits.h>
 #include <__string/constexpr_c_functions.h>
 #include <__type_traits/is_constant_evaluated.h>
@@ -355,34 +357,35 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char8_t>
         {return __c1 < __c2;}
 
   static _LIBCPP_HIDE_FROM_ABI constexpr int
-  compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
+  compare(const char_type* __s1, const char_type* __s2, size_t __n) noexcept {
       return std::__constexpr_memcmp(__s1, __s2, __element_count(__n));
   }
 
-    static _LIBCPP_HIDE_FROM_ABI constexpr
-    size_t           length(const char_type* __s) _NOEXCEPT;
+  static _LIBCPP_HIDE_FROM_ABI constexpr size_t length(const char_type* __str) noexcept {
+    return std::__constexpr_strlen(__str);
+  }
 
-    _LIBCPP_INLINE_VISIBILITY static constexpr
-    const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT;
+  static _LIBCPP_HIDE_FROM_ABI constexpr const char_type*
+  find(const char_type* __s, size_t __n, const char_type& __a) noexcept {
+    return std::__constexpr_memchr(__s, __a, __n);
+  }
 
-    static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
-    char_type*       move(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
-        return std::__constexpr_memmove(__s1, __s2, __element_count(__n));
-    }
+  static _LIBCPP_HIDE_FROM_ABI constexpr char_type*
+  move(char_type* __s1, const char_type* __s2, size_t __n) noexcept {
+    return std::__constexpr_memmove(__s1, __s2, __element_count(__n));
+  }
 
-    static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
-    char_type*       copy(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
-      _LIBCPP_ASSERT_NON_OVERLAPPING_RANGES(!std::__is_pointer_in_range(__s1, __s1 + __n, __s2),
-                                            "char_traits::copy: source and destination ranges overlap");
-      std::copy_n(__s2, __n, __s1);
-      return __s1;
-    }
+  static _LIBCPP_HIDE_FROM_ABI constexpr char_type* copy(char_type* __s1, const char_type* __s2, size_t __n) noexcept {
+    _LIBCPP_ASSERT_NON_OVERLAPPING_RANGES(!std::__is_pointer_in_range(__s1, __s1 + __n, __s2),
+                                          "char_traits::copy: source and destination ranges overlap");
+    std::copy_n(__s2, __n, __s1);
+    return __s1;
+  }
 
-    static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
-    char_type*       assign(char_type* __s, size_t __n, char_type __a) _NOEXCEPT {
-        std::fill_n(__s, __n, __a);
-        return __s;
-    }
+  static _LIBCPP_HIDE_FROM_ABI constexpr char_type* assign(char_type* __s, size_t __n, char_type __a) noexcept {
+    std::fill_n(__s, __n, __a);
+    return __s;
+  }
 
     static inline _LIBCPP_HIDE_FROM_ABI constexpr int_type  not_eof(int_type __c) noexcept
         {return eq_int_type(__c, eof()) ? ~eof() : __c;}
@@ -396,31 +399,6 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char8_t>
         {return int_type(EOF);}
 };
 
-// TODO use '__builtin_strlen' if it ever supports char8_t ??
-inline constexpr
-size_t
-char_traits<char8_t>::length(const char_type* __s) _NOEXCEPT
-{
-    size_t __len = 0;
-    for (; !eq(*__s, char_type(0)); ++__s)
-        ++__len;
-    return __len;
-}
-
-// TODO use '__builtin_char_memchr' if it ever supports char8_t ??
-inline constexpr
-const char8_t*
-char_traits<char8_t>::find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT
-{
-    for (; __n; --__n)
-    {
-        if (eq(*__s, __a))
-            return __s;
-        ++__s;
-    }
-    return nullptr;
-}
-
 #endif // _LIBCPP_HAS_NO_CHAR8_T
 
 template <>
@@ -446,8 +424,15 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char16_t>
     int              compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT;
     _LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
     size_t           length(const char_type* __s) _NOEXCEPT;
-    _LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
-    const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT;
+
+    static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 const char_type*
+    find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
+      __identity __proj;
+      const char_type* __match = std::__find_impl(__s, __s + __n, __a, __proj);
+      if (__match == __s + __n)
+        return nullptr;
+      return __match;
+    }
 
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20
     static char_type*       move(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
@@ -504,19 +489,6 @@ char_traits<char16_t>::length(const char_type* __s) _NOEXCEPT
     return __len;
 }
 
-inline _LIBCPP_CONSTEXPR_SINCE_CXX17
-const char16_t*
-char_traits<char16_t>::find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT
-{
-    for (; __n; --__n)
-    {
-        if (eq(*__s, __a))
-            return __s;
-        ++__s;
-    }
-    return nullptr;
-}
-
 template <>
 struct _LIBCPP_TEMPLATE_VIS char_traits<char32_t>
 {
@@ -540,8 +512,15 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char32_t>
     int              compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT;
     _LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
     size_t           length(const char_type* __s) _NOEXCEPT;
-    _LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
-    const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT;
+
+    static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 const char_type*
+    find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
+      __identity __proj;
+      const char_type* __match = std::__find_impl(__s, __s + __n, __a, __proj);
+      if (__match == __s + __n)
+        return nullptr;
+      return __match;
+    }
 
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20
     static char_type*       move(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
@@ -596,19 +575,6 @@ char_traits<char32_t>::length(const char_type* __s) _NOEXCEPT
     return __len;
 }
 
-inline _LIBCPP_CONSTEXPR_SINCE_CXX17
-const char32_t*
-char_traits<char32_t>::find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT
-{
-    for (; __n; --__n)
-    {
-        if (eq(*__s, __a))
-            return __s;
-        ++__s;
-    }
-    return nullptr;
-}
-
 // helper fns for basic_string and string_view
 
 // __str_find
diff --git a/libcxx/include/__string/constexpr_c_functions.h b/libcxx/include/__string/constexpr_c_functions.h
index 198f0f5e6809147..c029c2d29fec88c 100644
--- a/libcxx/include/__string/constexpr_c_functions.h
+++ b/libcxx/include/__string/constexpr_c_functions.h
@@ -35,18 +35,23 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 // of elements as opposed to a number of bytes.
 enum class __element_count : size_t {};
 
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 size_t __constexpr_strlen(const char* __str) {
+template <class _Tp>
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 size_t __constexpr_strlen(const _Tp* __str) _NOEXCEPT {
+  static_assert(
+      is_integral<_Tp>::value && sizeof(_Tp) == 1, "__constexpr_strlen only works with integral types of size 1");
   // GCC currently doesn't support __builtin_strlen for heap-allocated memory during constant evaluation.
   // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70816
-#ifdef _LIBCPP_COMPILER_GCC
   if (__libcpp_is_constant_evaluated()) {
+#if _LIBCPP_STD_VER >= 17 && defined(_LIBCPP_COMPILER_CLANG_BASED)
+    if constexpr (is_same_v<_Tp, char>)
+      return __builtin_strlen(__str);
+#endif
     size_t __i = 0;
     for (; __str[__i] != '\0'; ++__i)
       ;
     return __i;
   }
-#endif
-  return __builtin_strlen(__str);
+  return __builtin_strlen(reinterpret_cast<const char*>(__str));
 }
 
 // Because of __libcpp_is_trivially_lexicographically_comparable we know that comparing the object representations is



More information about the libcxx-commits mailing list