[libcxx-commits] [libcxx] [libc++] Refactor the sequence container benchmarks (PR #119763)
Peng Liu via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Dec 17 18:09:05 PST 2024
================
@@ -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();
----------------
winner245 wrote:
```suggestion
c.pop_back();
```
I would suggest erase the last rather than the middle element after each insertion, just as in the erase case where you insert at the end after each erase. My rationale is that erase/insert at the end is very cheap (O(1)) compared to the operation we are actually testing (O(n)). This way, we can avoid using `st.PauseTiming()` and `st.ResumeTiming()` in a single iteration benchmark test.
https://github.com/llvm/llvm-project/pull/119763
More information about the libcxx-commits
mailing list