Skip to content

Conversation

philnik777
Copy link
Contributor

@philnik777 philnik777 commented Sep 22, 2025

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

Copy link

github-actions bot commented Sep 22, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@philnik777 philnik777 force-pushed the optimize_string_find_single_char branch from dd1885a to f188376 Compare September 22, 2025 13:38
@philnik777 philnik777 marked this pull request as ready for review September 23, 2025 10:21
@philnik777 philnik777 requested a review from a team as a code owner September 23, 2025 10:21
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Sep 23, 2025
@llvmbot
Copy link
Member

llvmbot commented Sep 23, 2025

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/160076.diff

3 Files Affected:

  • (modified) libcxx/include/__string/char_traits.h (+7)
  • (modified) libcxx/test/benchmarks/containers/string.bench.cpp (+39)
  • (modified) libcxx/test/std/strings/basic.string/string.ops/string_find/string_size.pass.cpp (+17)
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..68f596b1be62a 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_string_literal(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")); // "b" is a string literal, it should be longer
+    benchmark::ClobberMemory();
+  }
+}
+
+BENCHMARK(BM_string_literal)->RangeMultiplier(2)->Range(8, 8 << 10);
+
+static void BM_char_literal(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')); // 'b' is a char literal, it should be faster
+    benchmark::ClobberMemory();
+  }
+}
+BENCHMARK(BM_char_literal)->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);

@philnik777 philnik777 force-pushed the optimize_string_find_single_char branch from f188376 to 57ff008 Compare September 24, 2025 08:45
@philnik777 philnik777 merged commit a769608 into llvm:main Sep 25, 2025
78 checks passed
@philnik777 philnik777 deleted the optimize_string_find_single_char branch September 25, 2025 07:29
mahesh-attarde pushed a commit to mahesh-attarde/llvm-project that referenced this pull request Oct 3, 2025
…d string has length 1 (llvm#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
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants