[libcxx-commits] [libcxx] [libc++] Refactor the sequence container benchmarks (PR #119763)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jan 16 09:54:42 PST 2025


================
@@ -0,0 +1,435 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_BENCHMARKS_CONTAINERS_CONTAINER_BENCHMARKS_H
+#define TEST_BENCHMARKS_CONTAINERS_CONTAINER_BENCHMARKS_H
+
+#include <cstddef>
+#include <iterator> // for std::next
+#include <ranges>   // for std::from_range
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+
+namespace ContainerBenchmarks {
+
+template <class Container>
+void DoNotOptimizeData(Container& c) {
+  if constexpr (requires { c.data(); }) {
+    benchmark::DoNotOptimize(c.data());
+  } else {
+    benchmark::DoNotOptimize(&c);
+  }
+}
+
+//
+// Sequence container operations
+//
+template <class Container>
+void BM_ctor_size(benchmark::State& st) {
+  auto size = st.range(0);
+  char buffer[sizeof(Container)];
+  for (auto _ : st) {
+    std::construct_at(reinterpret_cast<Container*>(buffer), size);
+    benchmark::DoNotOptimize(buffer);
+    st.PauseTiming();
+    std::destroy_at(reinterpret_cast<Container*>(buffer));
+    st.ResumeTiming();
+  }
+}
+
+template <class Container>
+void BM_ctor_size_value(benchmark::State& st) {
+  using ValueType = typename Container::value_type;
+  const auto size = st.range(0);
+  ValueType value{};
+  benchmark::DoNotOptimize(value);
+  char buffer[sizeof(Container)];
+  for (auto _ : st) {
+    std::construct_at(reinterpret_cast<Container*>(buffer), size, value);
+    benchmark::DoNotOptimize(buffer);
+    st.PauseTiming();
+    std::destroy_at(reinterpret_cast<Container*>(buffer));
+    st.ResumeTiming();
+  }
+}
+
+template <class Container>
+void BM_ctor_iter_iter(benchmark::State& st) {
+  using ValueType = typename Container::value_type;
+  const auto size = st.range(0);
+  std::vector<ValueType> in(size);
+  const auto begin = in.begin();
+  const auto end   = in.end();
+  benchmark::DoNotOptimize(in);
+  char buffer[sizeof(Container)];
+  for (auto _ : st) {
+    std::construct_at(reinterpret_cast<Container*>(buffer), begin, end);
+    benchmark::DoNotOptimize(buffer);
+    st.PauseTiming();
+    std::destroy_at(reinterpret_cast<Container*>(buffer));
+    st.ResumeTiming();
+  }
+}
+
+#if TEST_STD_VER >= 23
+template <class Container>
+void BM_ctor_from_range(benchmark::State& st) {
+  using ValueType = typename Container::value_type;
+  const auto size = st.range(0);
+  std::vector<ValueType> in(size);
+  benchmark::DoNotOptimize(in);
+  char buffer[sizeof(Container)];
+  for (auto _ : st) {
+    std::construct_at(reinterpret_cast<Container*>(buffer), std::from_range, in);
+    benchmark::DoNotOptimize(buffer);
+    st.PauseTiming();
+    std::destroy_at(reinterpret_cast<Container*>(buffer));
+    st.ResumeTiming();
+  }
+}
+#endif
+
+template <class Container>
+void BM_ctor_copy(benchmark::State& st) {
+  auto size = st.range(0);
+  Container c(size);
+  char buffer[sizeof(Container)];
+  for (auto _ : st) {
+    std::construct_at(reinterpret_cast<Container*>(buffer), c);
+    benchmark::DoNotOptimize(buffer);
+    st.PauseTiming();
+    std::destroy_at(reinterpret_cast<Container*>(buffer));
+    st.ResumeTiming();
+  }
+}
+
+template <class Container>
+void BM_assignment(benchmark::State& st) {
+  auto size = st.range(0);
+  Container c1;
+  Container c2(size);
+  for (auto _ : st) {
+    c1 = c2;
+    DoNotOptimizeData(c1);
+    DoNotOptimizeData(c2);
+  }
+}
+
+template <typename Container>
+void BM_assign_inputiter(benchmark::State& st) {
+  using ValueType = typename Container::value_type;
+  auto size       = st.range(0);
+  std::vector<ValueType> inputs(size);
+  Container c(inputs.begin(), inputs.end());
+  DoNotOptimizeData(c);
+  DoNotOptimizeData(inputs);
+  ValueType* first = inputs.data();
+  ValueType* last  = inputs.data() + inputs.size();
+
+  for (auto _ : st) {
+    c.assign(cpp17_input_iterator(first), cpp17_input_iterator(last));
+    benchmark::ClobberMemory();
+  }
+}
+
+template <class Container>
+void BM_insert_middle(benchmark::State& st) {
+  using ValueType = typename Container::value_type;
+  const int count = st.range(0);
+  std::vector<ValueType> inputs(count);
+  Container c(inputs.begin(), inputs.end());
+  DoNotOptimizeData(c);
+
+  ValueType value{};
+  benchmark::DoNotOptimize(value);
+
+  auto mid = std::next(c.begin(), count / 2);
+  for (auto _ : st) {
+    auto inserted = c.insert(mid, value);
+    DoNotOptimizeData(c);
+
+    st.PauseTiming();
+    mid = c.erase(inserted);
+    st.ResumeTiming();
+  }
+}
+
+template <class Container>
+void BM_insert_start(benchmark::State& st) {
+  using ValueType = typename Container::value_type;
+  const int count = st.range(0);
+  std::vector<ValueType> inputs(count);
+  Container c(inputs.begin(), inputs.end());
+  DoNotOptimizeData(c);
+
+  ValueType value{};
+  benchmark::DoNotOptimize(value);
+
+  for (auto _ : st) {
+    auto inserted = c.insert(c.begin(), value);
+    DoNotOptimizeData(c);
+
+    st.PauseTiming();
+    c.erase(inserted);
+    st.ResumeTiming();
+  }
+}
+
+template <class Container>
+void BM_erase_middle(benchmark::State& st) {
+  using ValueType = typename Container::value_type;
+  const int count = st.range(0);
+  std::vector<ValueType> inputs(count);
+  Container c(inputs.begin(), inputs.end());
+  DoNotOptimizeData(c);
+
+  ValueType value{};
+  benchmark::DoNotOptimize(value);
+
+  auto mid = std::next(c.begin(), count / 2);
+  for (auto _ : st) {
+    c.erase(mid);
+    DoNotOptimizeData(c);
+
+    st.PauseTiming();
+    c.insert(c.end(), value); // re-insert an element at the end to avoid needing a new container
+    mid = std::next(c.begin(), c.size() / 2);
+    st.ResumeTiming();
----------------
ldionne wrote:

Since this benchmark is used for many container types though, I feel it's safer to recompute. For example, `std::deque::erase` invalidates all iterators in most cases.

https://github.com/llvm/llvm-project/pull/119763


More information about the libcxx-commits mailing list