[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