[libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks (PR #185397)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Mon Mar 9 08:03:12 PDT 2026


================
@@ -6,602 +6,487 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
-#include "test_macros.h"
-
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-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();
+#include "make_string.h"
+
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
 
-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;
-    benchmark::DoNotOptimize(Default);
-  }
-}
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
 
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::basic_string()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
----------------
ldionne wrote:

```suggestion
  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
```

https://github.com/llvm/llvm-project/pull/185397


More information about the libcxx-commits mailing list