[libcxx-commits] [libcxx] a769608 - [libc++] Fold __search_substring into _Traits::find in case the second string has length 1 (#160076)

via libcxx-commits libcxx-commits at lists.llvm.org
Thu Sep 25 00:29:05 PDT 2025


Author: Nikolas Klauser
Date: 2025-09-25T09:29:01+02:00
New Revision: a769608339ad4fff68081ceb7f388da8b660cfa1

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

LOG: [libc++] Fold __search_substring into _Traits::find in case the second string has length 1 (#160076)

Apple M4:
```
Benchmark                                                      Baseline    Candidate    Difference    % Difference
-----------------------------------------------------------  ----------  -----------  ------------  --------------
BM_string_literal/1024                                            16.99        16.79         -0.21           -1.21
BM_string_literal/128                                              3.44         3.34         -0.10           -2.88
BM_string_literal/16                                               1.80         1.69         -0.11           -5.93
BM_string_literal/2048                                            38.44        38.38         -0.07           -0.17
BM_string_literal/256                                              5.77         5.65         -0.12           -2.02
BM_string_literal/32                                               2.03         1.92         -0.11           -5.44
BM_string_literal/4096                                            73.92        73.74         -0.18           -0.25
BM_string_literal/512                                              9.49         9.41         -0.08           -0.84
BM_string_literal/64                                               2.59         2.45         -0.14           -5.38
BM_string_literal/8                                                1.79         1.69         -0.11           -5.90
BM_string_literal/8192                                           132.09       131.81         -0.28           -0.21
```

Added: 
    

Modified: 
    libcxx/include/__string/char_traits.h
    libcxx/test/benchmarks/containers/string.bench.cpp
    libcxx/test/std/strings/basic.string/string.ops/string_find/string_size.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__string/char_traits.h b/libcxx/include/__string/char_traits.h
index 86c92477cbfeb..8292750919427 100644
--- a/libcxx/include/__string/char_traits.h
+++ b/libcxx/include/__string/char_traits.h
@@ -369,6 +369,13 @@ _LIBCPP_HIDE_FROM_ABI inline _LIBCPP_CONSTEXPR_SINCE_CXX14 const _CharT* __searc
   if (__len1 < __len2)
     return __last1;
 
+  if (__builtin_constant_p(__len2 == 1) && __len2 == 1) {
+    auto __res = _Traits::find(__first1, __len1, *__first2);
+    if (__res == nullptr)
+      return __last1;
+    return __res;
+  }
+
   // First element of __first2 is loop invariant.
   _CharT __f2 = *__first2;
   while (true) {

diff  --git a/libcxx/test/benchmarks/containers/string.bench.cpp b/libcxx/test/benchmarks/containers/string.bench.cpp
index 966775d31a8cf..2484ec8fd955f 100644
--- a/libcxx/test/benchmarks/containers/string.bench.cpp
+++ b/libcxx/test/benchmarks/containers/string.bench.cpp
@@ -60,6 +60,45 @@ static void BM_StringFindMatch2(benchmark::State& state) {
 }
 BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
 
+static void BM_StringFindStringLiteral(benchmark::State& state) {
+  std::string s;
+
+  for (int i = 0; i < state.range(0); i++)
+    s += 'a';
+
+  s += 'b';
+
+  benchmark::DoNotOptimize(s.data());
+  benchmark::ClobberMemory();
+  size_t pos;
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(pos = s.find("b"));
+    benchmark::ClobberMemory();
+  }
+}
+
+BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
+
+static void BM_StringFindCharLiteral(benchmark::State& state) {
+  std::string s;
+
+  for (int i = 0; i < state.range(0); i++)
+    s += 'a';
+
+  s += 'b';
+
+  benchmark::DoNotOptimize(s.data());
+  benchmark::ClobberMemory();
+  size_t pos;
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(pos = s.find('b'));
+    benchmark::ClobberMemory();
+  }
+}
+BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
+
 static void BM_StringCtorDefault(benchmark::State& state) {
   for (auto _ : state) {
     std::string Default;

diff  --git a/libcxx/test/std/strings/basic.string/string.ops/string_find/string_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.ops/string_find/string_size.pass.cpp
index 60ed469ce991b..4aa4a3f339142 100644
--- a/libcxx/test/std/strings/basic.string/string.ops/string_find/string_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.ops/string_find/string_size.pass.cpp
@@ -43,14 +43,17 @@ TEST_CONSTEXPR_CXX20 void test0() {
   test(S(""), S("abcdeabcde"), 1, S::npos);
   test(S(""), S("abcdeabcdeabcdeabcde"), 1, S::npos);
   test(S("abcde"), S(""), 0, 0);
+  test(S("abcde"), S("a"), 0, 0);
   test(S("abcde"), S("abcde"), 0, 0);
   test(S("abcde"), S("abcdeabcde"), 0, S::npos);
   test(S("abcde"), S("abcdeabcdeabcdeabcde"), 0, S::npos);
   test(S("abcde"), S(""), 1, 1);
+  test(S("abcde"), S("a"), 1, S::npos);
   test(S("abcde"), S("abcde"), 1, S::npos);
   test(S("abcde"), S("abcdeabcde"), 1, S::npos);
   test(S("abcde"), S("abcdeabcdeabcdeabcde"), 1, S::npos);
   test(S("abcde"), S(""), 2, 2);
+  test(S("abcde"), S("a"), 2, S::npos);
   test(S("abcde"), S("abcde"), 2, S::npos);
   test(S("abcde"), S("abcdeabcde"), 2, S::npos);
   test(S("abcde"), S("abcdeabcdeabcdeabcde"), 2, S::npos);
@@ -59,58 +62,72 @@ TEST_CONSTEXPR_CXX20 void test0() {
   test(S("abcde"), S("abcdeabcde"), 4, S::npos);
   test(S("abcde"), S("abcdeabcdeabcdeabcde"), 4, S::npos);
   test(S("abcde"), S(""), 5, 5);
+  test(S("abcde"), S("a"), 5, S::npos);
   test(S("abcde"), S("abcde"), 5, S::npos);
   test(S("abcde"), S("abcdeabcde"), 5, S::npos);
   test(S("abcde"), S("abcdeabcdeabcdeabcde"), 5, S::npos);
   test(S("abcde"), S(""), 6, S::npos);
+  test(S("abcde"), S("a"), 6, S::npos);
   test(S("abcde"), S("abcde"), 6, S::npos);
   test(S("abcde"), S("abcdeabcde"), 6, S::npos);
   test(S("abcde"), S("abcdeabcdeabcdeabcde"), 6, S::npos);
   test(S("abcdeabcde"), S(""), 0, 0);
+  test(S("abcdeabcde"), S("a"), 0, 0);
   test(S("abcdeabcde"), S("abcde"), 0, 0);
   test(S("abcdeabcde"), S("abcdeabcde"), 0, 0);
   test(S("abcdeabcde"), S("abcdeabcdeabcdeabcde"), 0, S::npos);
   test(S("abcdeabcde"), S(""), 1, 1);
+  test(S("abcdeabcde"), S("a"), 1, 5);
   test(S("abcdeabcde"), S("abcde"), 1, 5);
   test(S("abcdeabcde"), S("abcdeabcde"), 1, S::npos);
   test(S("abcdeabcde"), S("abcdeabcdeabcdeabcde"), 1, S::npos);
   test(S("abcdeabcde"), S(""), 5, 5);
+  test(S("abcdeabcde"), S("a"), 5, 5);
   test(S("abcdeabcde"), S("abcde"), 5, 5);
   test(S("abcdeabcde"), S("abcdeabcde"), 5, S::npos);
   test(S("abcdeabcde"), S("abcdeabcdeabcdeabcde"), 5, S::npos);
   test(S("abcdeabcde"), S(""), 9, 9);
+  test(S("abcdeabcde"), S("a"), 9, S::npos);
   test(S("abcdeabcde"), S("abcde"), 9, S::npos);
   test(S("abcdeabcde"), S("abcdeabcde"), 9, S::npos);
   test(S("abcdeabcde"), S("abcdeabcdeabcdeabcde"), 9, S::npos);
   test(S("abcdeabcde"), S(""), 10, 10);
+  test(S("abcdeabcde"), S("a"), 10, S::npos);
   test(S("abcdeabcde"), S("abcde"), 10, S::npos);
   test(S("abcdeabcde"), S("abcdeabcde"), 10, S::npos);
   test(S("abcdeabcde"), S("abcdeabcdeabcdeabcde"), 10, S::npos);
   test(S("abcdeabcde"), S(""), 11, S::npos);
+  test(S("abcdeabcde"), S("a"), 11, S::npos);
   test(S("abcdeabcde"), S("abcde"), 11, S::npos);
   test(S("abcdeabcde"), S("abcdeabcde"), 11, S::npos);
   test(S("abcdeabcde"), S("abcdeabcdeabcdeabcde"), 11, S::npos);
   test(S("abcdeabcdeabcdeabcde"), S(""), 0, 0);
+  test(S("abcdeabcdeabcdeabcde"), S("a"), 0, 0);
   test(S("abcdeabcdeabcdeabcde"), S("abcde"), 0, 0);
   test(S("abcdeabcdeabcdeabcde"), S("abcdeabcde"), 0, 0);
   test(S("abcdeabcdeabcdeabcde"), S("abcdeabcdeabcdeabcde"), 0, 0);
   test(S("abcdeabcdeabcdeabcde"), S(""), 1, 1);
+  test(S("abcdeabcdeabcdeabcde"), S("a"), 1, 5);
   test(S("abcdeabcdeabcdeabcde"), S("abcde"), 1, 5);
   test(S("abcdeabcdeabcdeabcde"), S("abcdeabcde"), 1, 5);
   test(S("abcdeabcdeabcdeabcde"), S("abcdeabcdeabcdeabcde"), 1, S::npos);
   test(S("abcdeabcdeabcdeabcde"), S(""), 10, 10);
+  test(S("abcdeabcdeabcdeabcde"), S("a"), 10, 10);
   test(S("abcdeabcdeabcdeabcde"), S("abcde"), 10, 10);
   test(S("abcdeabcdeabcdeabcde"), S("abcdeabcde"), 10, 10);
   test(S("abcdeabcdeabcdeabcde"), S("abcdeabcdeabcdeabcde"), 10, S::npos);
   test(S("abcdeabcdeabcdeabcde"), S(""), 19, 19);
+  test(S("abcdeabcdeabcdeabcde"), S("a"), 19, S::npos);
   test(S("abcdeabcdeabcdeabcde"), S("abcde"), 19, S::npos);
   test(S("abcdeabcdeabcdeabcde"), S("abcdeabcde"), 19, S::npos);
   test(S("abcdeabcdeabcdeabcde"), S("abcdeabcdeabcdeabcde"), 19, S::npos);
   test(S("abcdeabcdeabcdeabcde"), S(""), 20, 20);
+  test(S("abcdeabcdeabcdeabcde"), S("a"), 20, S::npos);
   test(S("abcdeabcdeabcdeabcde"), S("abcde"), 20, S::npos);
   test(S("abcdeabcdeabcdeabcde"), S("abcdeabcde"), 20, S::npos);
   test(S("abcdeabcdeabcdeabcde"), S("abcdeabcdeabcdeabcde"), 20, S::npos);
   test(S("abcdeabcdeabcdeabcde"), S(""), 21, S::npos);
+  test(S("abcdeabcdeabcdeabcde"), S("a"), 21, S::npos);
   test(S("abcdeabcdeabcdeabcde"), S("abcde"), 21, S::npos);
   test(S("abcdeabcdeabcdeabcde"), S("abcdeabcde"), 21, S::npos);
   test(S("abcdeabcdeabcdeabcde"), S("abcdeabcdeabcdeabcde"), 21, S::npos);


        


More information about the libcxx-commits mailing list