[libcxx-commits] [libcxx] [libc++][pstl] Add more benchmarks of the parallel algorithms (PR #225908)
Michael G. Kazakov via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Sep 26 02:50:38 PDT 2026
https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/225908
>From 99c2e8db681b8230021cedefb111e149365c901b Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 22 Sep 2026 21:49:20 +0100
Subject: [PATCH 1/5] Benchmark for std::find
---
.../nonmodifying/pstl.find.bench.cpp | 62 +++++++++++++++++++
1 file changed, 62 insertions(+)
create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/pstl.find.bench.cpp
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/pstl.find.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/pstl.find.bench.cpp
new file mode 100644
index 0000000000000..99cbe47b191be
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/pstl.find.bench.cpp
@@ -0,0 +1,62 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <vector>
+#include <execution>
+#include <utility>
+
+#include <benchmark/benchmark.h>
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+ auto bm = [](std::string name, auto&& policy, bool has_needle) {
+ benchmark::RegisterBenchmark(
+ name,
+ [&policy, has_needle](auto& st) mutable {
+ std::size_t size = st.range(0);
+ double x = Generate<double>::random();
+ double y = random_different_from({x});
+ std::vector<double> c(size, x);
+
+ if (has_needle) {
+ // put the element we're searching for at 25% of the sequence
+ *std::next(c.begin(), size / 4) = y;
+ }
+
+ for ([[maybe_unused]] auto _ : st) {
+ benchmark::DoNotOptimize(c);
+ benchmark::DoNotOptimize(y);
+ auto result = std::find(policy, c.begin(), c.end(), y);
+ benchmark::DoNotOptimize(result);
+ }
+ })
+ ->Arg(1 << 6) // 64
+ ->Arg(1 << 16) // 65'536
+ ->Arg(1 << 26) // 67'108'864
+ ->UseRealTime();
+ };
+#if defined(TEST_PSTL_ENABLE_SEQ_BASELINES)
+ bm("std::find(std::execution::seq, vector<double>) (bail 25%)", std::execution::seq, true);
+ bm("std::find(std::execution::seq, vector<double>) (process all)", std::execution::seq, false);
+#endif
+ bm("std::find(std::execution::par, vector<double>) (bail 25%)", std::execution::par, true);
+ bm("std::find(std::execution::par, vector<double>) (process all)", std::execution::par, false);
+
+ benchmark::Initialize(&argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+ benchmark::Shutdown();
+ return 0;
+}
>From c0776ea19989ff9637e8039d792ae8067e655693 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 22 Sep 2026 22:44:45 +0100
Subject: [PATCH 2/5] Benchmark for parallel std::transform_reduce()
---
.../numeric/pstl.transform_reduce.bench.cpp | 78 +++++++++++++++++++
1 file changed, 78 insertions(+)
create mode 100644 libcxx/test/benchmarks/numeric/pstl.transform_reduce.bench.cpp
diff --git a/libcxx/test/benchmarks/numeric/pstl.transform_reduce.bench.cpp b/libcxx/test/benchmarks/numeric/pstl.transform_reduce.bench.cpp
new file mode 100644
index 0000000000000..4e01a32166c78
--- /dev/null
+++ b/libcxx/test/benchmarks/numeric/pstl.transform_reduce.bench.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <cmath>
+#include <numeric>
+#include <string>
+#include <type_traits>
+#include <vector>
+#include <execution>
+#include <utility>
+
+#include <benchmark/benchmark.h>
+
+int main(int argc, char** argv) {
+ // A transformation/reduction that does just a bit more than a no-op
+ struct minimal {
+ double operator()(double x) const { return -x; }
+ double operator()(double x, double y) const { return x + y; }
+ };
+
+ // A transformation/reduction that does miniscule work per element
+ struct cheap {
+ double operator()(double x) const { return -x + 0.25; }
+ double operator()(double x, double y) const { return x / 2. * 2. / 2. * 2. + y / 2. * 2. / 2. * 2.; }
+ };
+
+ // A transformation/reduction that does significant work per element
+ struct expensive {
+ double operator()(double x) const { return (-x + 0.25) * (x + 10.); }
+ double operator()(double x, double y) const {
+ return x / 2. * 2. / 3. * 3. / 4. * 4. / 5. * 5. + y / 2. * 2. / 2. * 2. / 4. * 4. / 5. * 5.;
+ }
+ };
+
+ auto bm = [](std::string name, auto&& policy, auto func) {
+ benchmark::RegisterBenchmark(
+ name,
+ [&policy, func](auto& st) {
+ std::size_t size = st.range(0);
+ std::vector<double> c(size);
+ std::iota(c.begin(), c.end(), 1.);
+ for ([[maybe_unused]] auto _ : st) {
+ benchmark::ClobberMemory();
+ auto result = std::transform_reduce(policy, c.begin(), c.end(), 0.0, func, func);
+ benchmark::DoNotOptimize(result);
+ }
+ })
+ ->Arg(1 << 6) // 64
+ ->Arg(1 << 16) // 65'536
+ ->Arg(1 << 26) // 67'108'864
+ ->UseRealTime();
+ };
+#if defined(TEST_PSTL_ENABLE_SEQ_BASELINES)
+ bm("std::transform_reduce(std::execution::seq, vector<double>) (minimal)", std::execution::seq, minimal{});
+ bm("std::transform_reduce(std::execution::seq, vector<double>) (cheap)", std::execution::seq, cheap{});
+ bm("std::transform_reduce(std::execution::seq, vector<double>) (expensive)", std::execution::seq, expensive{});
+#endif
+ bm("std::transform_reduce(std::execution::par, vector<double>) (minimal)", std::execution::par, minimal{});
+ bm("std::transform_reduce(std::execution::par, vector<double>) (cheap)", std::execution::par, cheap{});
+ bm("std::transform_reduce(std::execution::par, vector<double>) (expensive)", std::execution::par, expensive{});
+
+ benchmark::Initialize(&argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+ benchmark::Shutdown();
+ return 0;
+}
>From ba0b0a8107d7f462cb2b668ede82f27b1d4d2580 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 23 Sep 2026 20:31:25 +0100
Subject: [PATCH 3/5] Benchmark for parallel std::sort()
---
.../algorithms/sorting/pstl.sort.bench.cpp | 72 +++++++++++++++++++
1 file changed, 72 insertions(+)
create mode 100644 libcxx/test/benchmarks/algorithms/sorting/pstl.sort.bench.cpp
diff --git a/libcxx/test/benchmarks/algorithms/sorting/pstl.sort.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/pstl.sort.bench.cpp
new file mode 100644
index 0000000000000..f972469cc22d0
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/pstl.sort.bench.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <vector>
+#include <execution>
+#include <utility>
+
+#include <benchmark/benchmark.h>
+
+#include "common.h"
+#include "test_macros.h"
+
+int main(int argc, char** argv) {
+ auto bm = [](std::string name, auto&& policy, auto generate_data) {
+ benchmark::RegisterBenchmark(
+ name,
+ [&policy, generate_data](auto& st) mutable {
+ std::size_t size = st.range(0);
+ std::vector<int> data = generate_data(size);
+ std::vector<int> c = data;
+
+ for ([[maybe_unused]] auto _ : st) {
+ benchmark::DoNotOptimize(c);
+ std::sort(policy, c.begin(), c.end());
+ benchmark::DoNotOptimize(c);
+
+ // Reset c to its original unsorted state
+ st.PauseTiming();
+ std::copy(data.begin(), data.end(), c.begin());
+ st.ResumeTiming();
+ }
+ })
+ ->Arg(1 << 6) // 64
+ ->Arg(1 << 16) // 65'536
+ ->Arg(1 << 26) // 67'108'864
+ ->UseRealTime();
+ };
+
+ auto register_bm = [&](auto generate, std::string variant) {
+ auto name = [variant](std::string op) { return op + " (" + variant + ")"; };
+#if defined(TEST_PSTL_ENABLE_SEQ_BASELINES)
+ bm.operator()(name("std::sort(std::execution::seq, vector<int>)"), std::execution::seq, generate);
+#endif
+ bm.operator()(name("std::sort(std::execution::par, vector<int>)"), std::execution::par, generate);
+ };
+
+ register_bm(support::quicksort_adversarial_data<int>, "qsort adversarial");
+ register_bm(support::ascending_sorted_data<int>, "ascending");
+ register_bm(support::descending_sorted_data<int>, "descending");
+ register_bm(support::pipe_organ_data<int>, "pipe-organ");
+ register_bm(support::heap_data<int>, "heap");
+ register_bm(support::shuffled_data<int>, "shuffled");
+ register_bm(support::single_element_data<int>, "repeated");
+
+ benchmark::Initialize(&argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+ benchmark::Shutdown();
+ return 0;
+}
>From fa5dd22ec54cd3f5dc0245058ad31ba2d2364ea1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 23 Sep 2026 21:53:35 +0100
Subject: [PATCH 4/5] Require C++20 for some helpers
---
.../test/benchmarks/algorithms/nonmodifying/pstl.find.bench.cpp | 2 +-
libcxx/test/benchmarks/algorithms/sorting/pstl.sort.bench.cpp | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/pstl.find.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/pstl.find.bench.cpp
index 99cbe47b191be..98642ad7aa87e 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/pstl.find.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/pstl.find.bench.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++17
+// REQUIRES: std-at-least-c++20
// UNSUPPORTED: libcpp-has-no-incomplete-pstl
diff --git a/libcxx/test/benchmarks/algorithms/sorting/pstl.sort.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/pstl.sort.bench.cpp
index f972469cc22d0..8a513206a473f 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/pstl.sort.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/pstl.sort.bench.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-// REQUIRES: std-at-least-c++17
+// REQUIRES: std-at-least-c++20
// UNSUPPORTED: libcpp-has-no-incomplete-pstl
>From e611d983cb78f28bf93972d5bab0f3dc3a6611fa Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 26 Sep 2026 10:50:18 +0100
Subject: [PATCH 5/5] Run sorting in batches
---
.../algorithms/sorting/pstl.sort.bench.cpp | 23 ++++++++++++-------
.../numeric/pstl.transform_reduce.bench.cpp | 6 ++++-
2 files changed, 20 insertions(+), 9 deletions(-)
diff --git a/libcxx/test/benchmarks/algorithms/sorting/pstl.sort.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/pstl.sort.bench.cpp
index 8a513206a473f..e080deb5fa722 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/pstl.sort.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/pstl.sort.bench.cpp
@@ -11,6 +11,7 @@
// UNSUPPORTED: libcpp-has-no-incomplete-pstl
#include <algorithm>
+#include <array>
#include <cstddef>
#include <cstdint>
#include <string>
@@ -28,18 +29,24 @@ int main(int argc, char** argv) {
benchmark::RegisterBenchmark(
name,
[&policy, generate_data](auto& st) mutable {
- std::size_t size = st.range(0);
- std::vector<int> data = generate_data(size);
- std::vector<int> c = data;
+ constexpr std::size_t BatchSize = 32;
+ std::size_t size = st.range(0);
+ std::vector<int> data = generate_data(size);
+ std::array<std::vector<int>, BatchSize> c;
+ std::fill_n(c.begin(), BatchSize, data);
- for ([[maybe_unused]] auto _ : st) {
- benchmark::DoNotOptimize(c);
- std::sort(policy, c.begin(), c.end());
- benchmark::DoNotOptimize(c);
+ while (st.KeepRunningBatch(BatchSize)) {
+ for (std::size_t i = 0; i != BatchSize; ++i) {
+ benchmark::DoNotOptimize(c[i]);
+ std::sort(policy, c[i].begin(), c[i].end());
+ benchmark::DoNotOptimize(c[i]);
+ }
// Reset c to its original unsorted state
st.PauseTiming();
- std::copy(data.begin(), data.end(), c.begin());
+ for (std::size_t i = 0; i != BatchSize; ++i) {
+ std::copy(data.begin(), data.end(), c[i].begin());
+ }
st.ResumeTiming();
}
})
diff --git a/libcxx/test/benchmarks/numeric/pstl.transform_reduce.bench.cpp b/libcxx/test/benchmarks/numeric/pstl.transform_reduce.bench.cpp
index 4e01a32166c78..9ae2ad3cc0860 100644
--- a/libcxx/test/benchmarks/numeric/pstl.transform_reduce.bench.cpp
+++ b/libcxx/test/benchmarks/numeric/pstl.transform_reduce.bench.cpp
@@ -33,13 +33,17 @@ int main(int argc, char** argv) {
// A transformation/reduction that does miniscule work per element
struct cheap {
double operator()(double x) const { return -x + 0.25; }
- double operator()(double x, double y) const { return x / 2. * 2. / 2. * 2. + y / 2. * 2. / 2. * 2.; }
+ double operator()(double x, double y) const {
+ // (compilers aren't allowed to optimize away the FP divisions and multiplications without fast-math)
+ return x / 2. * 2. / 2. * 2. + y / 2. * 2. / 2. * 2.;
+ }
};
// A transformation/reduction that does significant work per element
struct expensive {
double operator()(double x) const { return (-x + 0.25) * (x + 10.); }
double operator()(double x, double y) const {
+ // (compilers aren't allowed to optimize away the FP divisions and multiplications without fast-math)
return x / 2. * 2. / 3. * 3. / 4. * 4. / 5. * 5. + y / 2. * 2. / 2. * 2. / 4. * 4. / 5. * 5.;
}
};
More information about the libcxx-commits
mailing list