[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