[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