[libcxx-commits] [libcxx] [libc++] Use a union for uninitialized storage in associative container benchmarks (PR #227068)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Mon Sep 28 10:56:21 PDT 2026


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/227068

This removes the need for reinterpret_cast when accessing the containers constructed in the scratch space, and fixes the PMR constructor benchmark passing the wrong pointer to DoNotOptimize.

>From 86600a5a5973cb5d7ab4376877684539288c5653 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 28 Sep 2026 13:45:17 -0400
Subject: [PATCH] [libc++] Use a union for uninitialized storage in associative
 container benchmarks

This removes the need for reinterpret_cast when accessing the containers
constructed in the scratch space, and fixes the PMR constructor benchmark
passing the wrong pointer to DoNotOptimize.
---
 .../associative_container_benchmarks.h        | 53 ++++++++++---------
 1 file changed, 29 insertions(+), 24 deletions(-)

diff --git a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
index 0291c650d891c..5d60ef8c3352b 100644
--- a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
@@ -11,6 +11,7 @@
 
 #include <algorithm>
 #include <iterator>
+#include <memory>
 #include <memory_resource>
 #include <random>
 #include <string>
@@ -39,6 +40,14 @@ struct adapt_operations {
   // using rebind_alloc = ...;
 };
 
+// Uninitialized storage for N objects of type T, whose lifetime is managed manually.
+template <class T, std::size_t N>
+union ScratchSpace {
+  ScratchSpace() {}
+  ~ScratchSpace() {}
+  T elems[N];
+};
+
 template <class Container>
 void associative_container_benchmarks(std::string container) {
   using Key   = typename Container::key_type;
@@ -82,10 +91,6 @@ void associative_container_benchmarks(std::string container) {
   // PauseTiming() and ResumeTiming().
   static constexpr std::size_t BatchSize = 32;
 
-  struct alignas(Container) ScratchSpace {
-    char storage[sizeof(Container)];
-  };
-
   /////////////////////////
   // Constructors
   /////////////////////////
@@ -93,18 +98,18 @@ void associative_container_benchmarks(std::string container) {
     const std::size_t size = st.range(0);
     std::vector<Value> in  = make_value_types(generate_unique_keys(size));
     Container src(in.begin(), in.end());
-    ScratchSpace c[BatchSize];
+    ScratchSpace<Container, BatchSize> c;
 
     while (st.KeepRunningBatch(BatchSize)) {
       for (std::size_t i = 0; i != BatchSize; ++i) {
-        new (c + i) Container(src);
-        benchmark::DoNotOptimize(c + i);
+        std::construct_at(c.elems + i, src);
+        benchmark::DoNotOptimize(c.elems + i);
         benchmark::ClobberMemory();
       }
 
       st.PauseTiming();
       for (std::size_t i = 0; i != BatchSize; ++i) {
-        reinterpret_cast<Container*>(c + i)->~Container();
+        std::destroy_at(c.elems + i);
       }
       st.ResumeTiming();
     }
@@ -114,18 +119,18 @@ void associative_container_benchmarks(std::string container) {
     const std::size_t size = st.range(0);
     std::vector<Value> in  = make_value_types(generate_unique_keys(size));
     Container src(in.begin(), in.end());
-    ScratchSpace c[BatchSize];
+    ScratchSpace<Container, BatchSize> c;
 
     while (st.KeepRunningBatch(BatchSize)) {
       for (std::size_t i = 0; i != BatchSize; ++i) {
-        new (c + i) Container(src, std::allocator<typename Container::value_type>());
-        benchmark::DoNotOptimize(c + i);
+        std::construct_at(c.elems + i, src, std::allocator<typename Container::value_type>());
+        benchmark::DoNotOptimize(c.elems + i);
         benchmark::ClobberMemory();
       }
 
       st.PauseTiming();
       for (std::size_t i = 0; i != BatchSize; ++i) {
-        reinterpret_cast<Container*>(c + i)->~Container();
+        std::destroy_at(c.elems + i);
       }
       st.ResumeTiming();
     }
@@ -142,19 +147,19 @@ void associative_container_benchmarks(std::string container) {
     srcs.reserve(BatchSize);
     for (size_t i = 0; i != BatchSize; ++i)
       srcs.emplace_back(&rs).insert(in.begin(), in.end());
-    alignas(PMRContainer) char c[BatchSize * sizeof(PMRContainer)];
+    ScratchSpace<PMRContainer, BatchSize> c;
 
     std::pmr::monotonic_buffer_resource rs2(size * 64 * BatchSize); // 64 bytes should be enough per node
     while (st.KeepRunningBatch(BatchSize)) {
       for (std::size_t i = 0; i != BatchSize; ++i) {
-        new (c + i * sizeof(PMRContainer)) PMRContainer(std::move(srcs[i]), &rs2);
-        benchmark::DoNotOptimize(c + i);
+        std::construct_at(c.elems + i, std::move(srcs[i]), &rs2);
+        benchmark::DoNotOptimize(c.elems + i);
         benchmark::ClobberMemory();
       }
 
       st.PauseTiming();
       for (std::size_t i = 0; i != BatchSize; ++i) {
-        reinterpret_cast<PMRContainer*>(c + i * sizeof(PMRContainer))->~PMRContainer();
+        std::destroy_at(c.elems + i);
       }
       rs2.release();
       srcs.clear();
@@ -171,18 +176,18 @@ void associative_container_benchmarks(std::string container) {
     std::vector<Key> keys = generate_unique_keys(size);
     std::shuffle(keys.begin(), keys.end(), randomness);
     std::vector<Value> in = make_value_types(keys);
-    ScratchSpace c[BatchSize];
+    ScratchSpace<Container, BatchSize> c;
 
     while (st.KeepRunningBatch(BatchSize)) {
       for (std::size_t i = 0; i != BatchSize; ++i) {
-        new (c + i) Container(in.begin(), in.end());
-        benchmark::DoNotOptimize(c + i);
+        std::construct_at(c.elems + i, in.begin(), in.end());
+        benchmark::DoNotOptimize(c.elems + i);
         benchmark::ClobberMemory();
       }
 
       st.PauseTiming();
       for (std::size_t i = 0; i != BatchSize; ++i) {
-        reinterpret_cast<Container*>(c + i)->~Container();
+        std::destroy_at(c.elems + i);
       }
       st.ResumeTiming();
     }
@@ -193,18 +198,18 @@ void associative_container_benchmarks(std::string container) {
     std::vector<Key> keys  = generate_unique_keys(size);
     std::sort(keys.begin(), keys.end());
     std::vector<Value> in = make_value_types(keys);
-    ScratchSpace c[BatchSize];
+    ScratchSpace<Container, BatchSize> c;
 
     while (st.KeepRunningBatch(BatchSize)) {
       for (std::size_t i = 0; i != BatchSize; ++i) {
-        new (c + i) Container(in.begin(), in.end());
-        benchmark::DoNotOptimize(c + i);
+        std::construct_at(c.elems + i, in.begin(), in.end());
+        benchmark::DoNotOptimize(c.elems + i);
         benchmark::ClobberMemory();
       }
 
       st.PauseTiming();
       for (std::size_t i = 0; i != BatchSize; ++i) {
-        reinterpret_cast<Container*>(c + i)->~Container();
+        std::destroy_at(c.elems + i);
       }
       st.ResumeTiming();
     }



More information about the libcxx-commits mailing list