[libcxx-commits] [libcxx] [libc++] Add benchmark for `std::exception_ptr` (PR #164278)
Adrian Vogelsgesang via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Oct 21 04:51:21 PDT 2025
https://github.com/vogelsgesang updated https://github.com/llvm/llvm-project/pull/164278
>From 47823b92f0e160893e34b339c92e1c455d69cbea Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <avogelsgesang at salesforce.com>
Date: Mon, 20 Oct 2025 13:47:44 +0000
Subject: [PATCH 1/5] [libc++] Add benchmark for `std::exception_ptr`
This commit adds benchmarks for `std::exception_ptr` to set a baseline
in preparation for follow-up optimizations.
---
.../test/benchmarks/exception_ptr.bench.cpp | 45 +++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/libcxx/test/benchmarks/exception_ptr.bench.cpp b/libcxx/test/benchmarks/exception_ptr.bench.cpp
index 7791c510b1eb6..1ff1d8b10d2b0 100644
--- a/libcxx/test/benchmarks/exception_ptr.bench.cpp
+++ b/libcxx/test/benchmarks/exception_ptr.bench.cpp
@@ -18,4 +18,49 @@ void bm_make_exception_ptr(benchmark::State& state) {
}
BENCHMARK(bm_make_exception_ptr)->ThreadRange(1, 8);
+static bool exception_ptr_moves_copies_swap(std::exception_ptr p1) {
+ // Taken from https://github.com/llvm/llvm-project/issues/44892
+ std::exception_ptr p2(p1); // Copy constructor
+ std::exception_ptr p3(std::move(p2)); // Move constructor
+ p2 = std::move(p1); // Move assignment
+ p1 = p2; // Copy assignment
+ swap(p1, p2); // Swap
+ // Comparisons against nullptr. The overhead from creating temporary `exception_ptr`
+ // instances should be optimized out.
+ bool is_null = p1 == nullptr && nullptr == p2;
+ bool is_equal = p1 == p2; // Comparison
+ return is_null && is_equal;
+}
+
+void bm_nonnull_exception_ptr(benchmark::State& state) {
+ std::exception_ptr excptr = std::make_exception_ptr(42);
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(excptr);
+ benchmark::DoNotOptimize(exception_ptr_moves_copies_swap(excptr));
+ }
+}
+BENCHMARK(bm_nonnull_exception_ptr);
+
+void bm_null_exception_ptr(benchmark::State& state) {
+ std::exception_ptr excptr;
+ for (auto _ : state) {
+ // All of the `exception_ptr_noops` are no-ops but the optimizer
+ // cannot optimize them away, because the `DoNotOptimize` calls
+ // prevent the optimizer from doing so.
+ benchmark::DoNotOptimize(excptr);
+ benchmark::DoNotOptimize(exception_ptr_moves_copies_swap(excptr));
+ }
+}
+BENCHMARK(bm_null_exception_ptr);
+
+void bm_optimized_null_exception_ptr(benchmark::State& state) {
+ for (auto _ : state) {
+ // All of the `exception_ptr_noops` are no-ops because
+ // the exception_ptr is empty. Hence, the compiler should
+ // be able to optimize them very aggressively.
+ benchmark::DoNotOptimize(exception_ptr_moves_copies_swap(std::exception_ptr{nullptr}));
+ }
+}
+BENCHMARK(bm_optimized_null_exception_ptr);
+
BENCHMARK_MAIN();
>From 2da884557c493cdc10ea5c34cce92bffad78b9f1 Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <avogelsgesang at salesforce.com>
Date: Mon, 20 Oct 2025 21:11:04 +0000
Subject: [PATCH 2/5] Update comments
---
libcxx/test/benchmarks/exception_ptr.bench.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/libcxx/test/benchmarks/exception_ptr.bench.cpp b/libcxx/test/benchmarks/exception_ptr.bench.cpp
index 1ff1d8b10d2b0..467deeb3fd96d 100644
--- a/libcxx/test/benchmarks/exception_ptr.bench.cpp
+++ b/libcxx/test/benchmarks/exception_ptr.bench.cpp
@@ -32,6 +32,7 @@ static bool exception_ptr_moves_copies_swap(std::exception_ptr p1) {
return is_null && is_equal;
}
+// Benchmark copies, moves and comparisons of a non-null exception_ptr.
void bm_nonnull_exception_ptr(benchmark::State& state) {
std::exception_ptr excptr = std::make_exception_ptr(42);
for (auto _ : state) {
@@ -41,23 +42,22 @@ void bm_nonnull_exception_ptr(benchmark::State& state) {
}
BENCHMARK(bm_nonnull_exception_ptr);
+// Benchmark copies, moves and comparisons of a nullptr exception_ptr
+// where the compiler cannot prove that the exception_ptr is always
+// a nullptr and needs to emit runtime checks.
void bm_null_exception_ptr(benchmark::State& state) {
std::exception_ptr excptr;
for (auto _ : state) {
- // All of the `exception_ptr_noops` are no-ops but the optimizer
- // cannot optimize them away, because the `DoNotOptimize` calls
- // prevent the optimizer from doing so.
benchmark::DoNotOptimize(excptr);
benchmark::DoNotOptimize(exception_ptr_moves_copies_swap(excptr));
}
}
BENCHMARK(bm_null_exception_ptr);
+// Benchmark copies, moves and comparisons of a nullptr exception_ptr
+// where the compiler can proof that the exception_ptr is always a nullptr.
void bm_optimized_null_exception_ptr(benchmark::State& state) {
for (auto _ : state) {
- // All of the `exception_ptr_noops` are no-ops because
- // the exception_ptr is empty. Hence, the compiler should
- // be able to optimize them very aggressively.
benchmark::DoNotOptimize(exception_ptr_moves_copies_swap(std::exception_ptr{nullptr}));
}
}
>From bb6480e8de53232fee727053b5f9955df67af3fd Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <adrian.vogelsgesang at tum.de>
Date: Tue, 21 Oct 2025 11:59:33 +0200
Subject: [PATCH 3/5] update link in comment
Co-authored-by: Nikolas Klauser <nikolasklauser at berlin.de>
---
libcxx/test/benchmarks/exception_ptr.bench.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/test/benchmarks/exception_ptr.bench.cpp b/libcxx/test/benchmarks/exception_ptr.bench.cpp
index 467deeb3fd96d..fe754898f0c96 100644
--- a/libcxx/test/benchmarks/exception_ptr.bench.cpp
+++ b/libcxx/test/benchmarks/exception_ptr.bench.cpp
@@ -19,7 +19,7 @@ void bm_make_exception_ptr(benchmark::State& state) {
BENCHMARK(bm_make_exception_ptr)->ThreadRange(1, 8);
static bool exception_ptr_moves_copies_swap(std::exception_ptr p1) {
- // Taken from https://github.com/llvm/llvm-project/issues/44892
+ // Taken from https://llvm.org/PR45547
std::exception_ptr p2(p1); // Copy constructor
std::exception_ptr p3(std::move(p2)); // Move constructor
p2 = std::move(p1); // Move assignment
>From a799f0e92534d254ce1bca431c43acf2bfb4f23c Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <avogelsgesang at salesforce.com>
Date: Tue, 21 Oct 2025 10:52:47 +0000
Subject: [PATCH 4/5] Remove some comments
---
libcxx/test/benchmarks/exception_ptr.bench.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/libcxx/test/benchmarks/exception_ptr.bench.cpp b/libcxx/test/benchmarks/exception_ptr.bench.cpp
index fe754898f0c96..c3a3aa3e044c9 100644
--- a/libcxx/test/benchmarks/exception_ptr.bench.cpp
+++ b/libcxx/test/benchmarks/exception_ptr.bench.cpp
@@ -20,15 +20,15 @@ BENCHMARK(bm_make_exception_ptr)->ThreadRange(1, 8);
static bool exception_ptr_moves_copies_swap(std::exception_ptr p1) {
// Taken from https://llvm.org/PR45547
- std::exception_ptr p2(p1); // Copy constructor
- std::exception_ptr p3(std::move(p2)); // Move constructor
- p2 = std::move(p1); // Move assignment
- p1 = p2; // Copy assignment
- swap(p1, p2); // Swap
+ std::exception_ptr p2(p1);
+ std::exception_ptr p3(std::move(p2));
+ p2 = std::move(p1);
+ p1 = p2;
+ swap(p1, p2);
// Comparisons against nullptr. The overhead from creating temporary `exception_ptr`
// instances should be optimized out.
bool is_null = p1 == nullptr && nullptr == p2;
- bool is_equal = p1 == p2; // Comparison
+ bool is_equal = p1 == p2;
return is_null && is_equal;
}
>From 25817e26073b2743eda3af648911e8dffb46d653 Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <avogelsgesang at salesforce.com>
Date: Tue, 21 Oct 2025 11:29:51 +0000
Subject: [PATCH 5/5] Add benchmarks for individual operations
---
.../test/benchmarks/exception_ptr.bench.cpp | 129 ++++++++++++++++--
1 file changed, 119 insertions(+), 10 deletions(-)
diff --git a/libcxx/test/benchmarks/exception_ptr.bench.cpp b/libcxx/test/benchmarks/exception_ptr.bench.cpp
index c3a3aa3e044c9..9f55b2efcbc15 100644
--- a/libcxx/test/benchmarks/exception_ptr.bench.cpp
+++ b/libcxx/test/benchmarks/exception_ptr.bench.cpp
@@ -18,7 +18,116 @@ void bm_make_exception_ptr(benchmark::State& state) {
}
BENCHMARK(bm_make_exception_ptr)->ThreadRange(1, 8);
-static bool exception_ptr_moves_copies_swap(std::exception_ptr p1) {
+void bm_exception_ptr_copy_ctor_nonnull(benchmark::State& state) {
+ std::exception_ptr excptr = std::make_exception_ptr(42);
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(excptr);
+ benchmark::DoNotOptimize(std::exception_ptr(excptr));
+ }
+}
+BENCHMARK(bm_exception_ptr_copy_ctor_nonnull);
+
+void bm_exception_ptr_copy_ctor_null(benchmark::State& state) {
+ std::exception_ptr excptr = nullptr;
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(excptr);
+ benchmark::DoNotOptimize(std::exception_ptr(excptr));
+ }
+}
+BENCHMARK(bm_exception_ptr_copy_ctor_null);
+
+
+void bm_exception_ptr_move_ctor_nonnull(benchmark::State& state) {
+ std::exception_ptr excptr = std::make_exception_ptr(42);
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(excptr);
+ benchmark::DoNotOptimize(std::exception_ptr(std::move(excptr)));
+ }
+}
+BENCHMARK(bm_exception_ptr_move_ctor_nonnull);
+
+void bm_exception_ptr_move_ctor_null(benchmark::State& state) {
+ std::exception_ptr excptr = nullptr;
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(excptr);
+ benchmark::DoNotOptimize(std::exception_ptr(std::move(excptr)));
+ }
+}
+BENCHMARK(bm_exception_ptr_move_ctor_null);
+
+void bm_exception_ptr_copy_assign_nonnull(benchmark::State& state) {
+ std::exception_ptr excptr = std::make_exception_ptr(42);
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(excptr);
+ std::exception_ptr new_excptr;
+ new_excptr = excptr;
+ benchmark::DoNotOptimize(new_excptr);
+ }
+}
+BENCHMARK(bm_exception_ptr_copy_assign_nonnull);
+
+void bm_exception_ptr_copy_assign_null(benchmark::State& state) {
+ std::exception_ptr excptr = nullptr;
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(excptr);
+ std::exception_ptr new_excptr;
+ new_excptr = excptr;
+ benchmark::DoNotOptimize(new_excptr);
+ }
+}
+BENCHMARK(bm_exception_ptr_copy_assign_null);
+
+
+void bm_exception_ptr_move_assign_nonnull(benchmark::State& state) {
+ std::exception_ptr excptr = std::make_exception_ptr(42);
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(excptr);
+ std::exception_ptr new_excptr;
+ new_excptr = std::move(excptr);
+ benchmark::DoNotOptimize(new_excptr);
+ }
+}
+BENCHMARK(bm_exception_ptr_move_assign_nonnull);
+
+void bm_exception_ptr_move_assign_null(benchmark::State& state) {
+ std::exception_ptr excptr = nullptr;
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(excptr);
+ std::exception_ptr new_excptr;
+ new_excptr = std::move(excptr);
+ benchmark::DoNotOptimize(new_excptr);
+ }
+}
+BENCHMARK(bm_exception_ptr_move_assign_null);
+
+void bm_exception_ptr_swap_nonnull(benchmark::State& state) {
+ std::exception_ptr excptr = std::make_exception_ptr(42);
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(excptr);
+ std::exception_ptr new_excptr;
+ swap(excptr, new_excptr);
+ benchmark::DoNotOptimize(new_excptr);
+ }
+}
+BENCHMARK(bm_exception_ptr_swap_nonnull);
+
+void bm_exception_ptr_swap_null(benchmark::State& state) {
+ std::exception_ptr excptr = nullptr;
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(excptr);
+ std::exception_ptr new_excptr;
+ swap(excptr, new_excptr);
+ benchmark::DoNotOptimize(new_excptr);
+ }
+}
+BENCHMARK(bm_exception_ptr_swap_null);
+
+// A chain of moves, copies and swaps.
+// In contrast to the previous benchmarks of individual operations,
+// this benchmark performs a chain of operations. It thereby
+// specifically stresses the information available to the compiler
+// for optimizations from the header itself.
+static bool exception_ptr_move_copy_swap(std::exception_ptr p1) {
// Taken from https://llvm.org/PR45547
std::exception_ptr p2(p1);
std::exception_ptr p3(std::move(p2));
@@ -33,34 +142,34 @@ static bool exception_ptr_moves_copies_swap(std::exception_ptr p1) {
}
// Benchmark copies, moves and comparisons of a non-null exception_ptr.
-void bm_nonnull_exception_ptr(benchmark::State& state) {
+void bm_exception_ptr_move_copy_swap_nonnull(benchmark::State& state) {
std::exception_ptr excptr = std::make_exception_ptr(42);
for (auto _ : state) {
benchmark::DoNotOptimize(excptr);
- benchmark::DoNotOptimize(exception_ptr_moves_copies_swap(excptr));
+ benchmark::DoNotOptimize(exception_ptr_move_copy_swap(excptr));
}
}
-BENCHMARK(bm_nonnull_exception_ptr);
+BENCHMARK(bm_exception_ptr_move_copy_swap_nonnull);
// Benchmark copies, moves and comparisons of a nullptr exception_ptr
// where the compiler cannot prove that the exception_ptr is always
// a nullptr and needs to emit runtime checks.
-void bm_null_exception_ptr(benchmark::State& state) {
+void bm_exception_ptr_move_copy_swap_null(benchmark::State& state) {
std::exception_ptr excptr;
for (auto _ : state) {
benchmark::DoNotOptimize(excptr);
- benchmark::DoNotOptimize(exception_ptr_moves_copies_swap(excptr));
+ benchmark::DoNotOptimize(exception_ptr_move_copy_swap(excptr));
}
}
-BENCHMARK(bm_null_exception_ptr);
+BENCHMARK(bm_exception_ptr_move_copy_swap_null);
// Benchmark copies, moves and comparisons of a nullptr exception_ptr
// where the compiler can proof that the exception_ptr is always a nullptr.
-void bm_optimized_null_exception_ptr(benchmark::State& state) {
+void bm_exception_ptr_move_copy_swap_null_optimized(benchmark::State& state) {
for (auto _ : state) {
- benchmark::DoNotOptimize(exception_ptr_moves_copies_swap(std::exception_ptr{nullptr}));
+ benchmark::DoNotOptimize(exception_ptr_move_copy_swap(std::exception_ptr{nullptr}));
}
}
-BENCHMARK(bm_optimized_null_exception_ptr);
+BENCHMARK(bm_exception_ptr_move_copy_swap_null_optimized);
BENCHMARK_MAIN();
More information about the libcxx-commits
mailing list