blob: ee4d3c150f7966855891cdc4f211f97b42ec835e [file] [log] [blame]
// Copyright 2025 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cstddef>
#include <cstdint>
#include <deque>
#include <forward_list>
#include <list>
#include <random>
#include "absl/container/chunked_queue.h"
#include "absl/random/random.h"
#include "absl/status/status.h"
#include "absl/strings/cord.h"
#include "benchmark/benchmark.h"
namespace {
// Queue implementation using std::forward_list. Used to benchmark
// absl::chunked_queue against another plausable implementation.
template <typename T>
class forward_list_queue {
public:
using iterator = typename std::forward_list<T>::iterator;
forward_list_queue() = default;
~forward_list_queue() = default;
template <typename... Args>
void emplace_back(Args&&... args) {
if (list_.empty()) {
list_.emplace_front(std::forward<Args>(args)...);
tail_ = list_.begin();
} else {
list_.emplace_after(tail_, std::forward<Args>(args)...);
++tail_;
}
}
void push_back(const T& value) { emplace_back(value); }
iterator begin() { return list_.begin(); }
iterator end() { return list_.end(); }
T& front() { return list_.front(); }
const T& front() const { return list_.front(); }
void pop_front() { list_.pop_front(); }
bool empty() const { return list_.empty(); }
void clear() { list_.clear(); }
private:
std::forward_list<T> list_;
typename std::forward_list<T>::iterator tail_;
};
template <class T>
using Deque = std::deque<T>;
template <class T>
using List = std::list<T>;
template <class T>
using FwdList = forward_list_queue<T>;
template <class T>
using Chunked = absl::chunked_queue<T>;
template <class T>
using ExpChunked = absl::chunked_queue<T, 2, 64>;
class Element {
public:
Element() : Element(-1) {}
Element(int type) : type_(type) {} // NOLINT
operator int() const { return type_; } // NOLINT
private:
int type_;
absl::Cord item_;
absl::Status status_;
};
template <class Q>
Q MakeQueue(int64_t num_elements) {
Q q;
for (int64_t i = 0; i < num_elements; i++) {
q.push_back(static_cast<int>(i));
}
return q;
}
void CustomArgs(benchmark::internal::Benchmark* b) {
b->Arg(1 << 4);
b->Arg(1 << 10);
b->Arg(1 << 17);
}
template <class Q>
void BM_construct(benchmark::State& state) {
for (auto s : state) {
Q q;
benchmark::DoNotOptimize(q);
}
}
BENCHMARK_TEMPLATE(BM_construct, Deque<int64_t>);
BENCHMARK_TEMPLATE(BM_construct, List<int64_t>);
BENCHMARK_TEMPLATE(BM_construct, FwdList<int64_t>);
BENCHMARK_TEMPLATE(BM_construct, Chunked<int64_t>);
BENCHMARK_TEMPLATE(BM_construct, ExpChunked<int64_t>);
BENCHMARK_TEMPLATE(BM_construct, Deque<Element>);
BENCHMARK_TEMPLATE(BM_construct, List<Element>);
BENCHMARK_TEMPLATE(BM_construct, FwdList<Element>);
BENCHMARK_TEMPLATE(BM_construct, Chunked<Element>);
BENCHMARK_TEMPLATE(BM_construct, ExpChunked<Element>);
template <class Q>
void BM_destroy(benchmark::State& state) {
const int64_t num_elements = state.range(0);
for (auto s : state) {
state.PauseTiming();
{
Q q = MakeQueue<Q>(num_elements);
benchmark::DoNotOptimize(q);
state.ResumeTiming();
}
}
state.SetItemsProcessed(state.iterations() * num_elements);
}
BENCHMARK_TEMPLATE(BM_destroy, Deque<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_destroy, List<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_destroy, FwdList<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_destroy, Chunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_destroy, ExpChunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_destroy, Deque<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_destroy, List<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_destroy, FwdList<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_destroy, Chunked<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_destroy, ExpChunked<Element>)->Apply(CustomArgs);
template <class Q>
void BM_push_back(benchmark::State& state) {
const int64_t num_elements = state.range(0);
state.SetItemsProcessed(state.max_iterations * num_elements);
for (auto s : state) {
state.PauseTiming();
Q q;
state.ResumeTiming();
for (int j = 0; j < num_elements; j++) q.push_back(j);
benchmark::DoNotOptimize(q);
}
}
BENCHMARK_TEMPLATE(BM_push_back, Deque<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_push_back, List<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_push_back, FwdList<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_push_back, Chunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_push_back, ExpChunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_push_back, Deque<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_push_back, List<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_push_back, FwdList<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_push_back, Chunked<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_push_back, ExpChunked<Element>)->Apply(CustomArgs);
template <class Q>
void BM_pop_front(benchmark::State& state) {
const int64_t num_elements = state.range(0);
state.SetItemsProcessed(state.max_iterations * num_elements);
for (auto s : state) {
state.PauseTiming();
Q q = MakeQueue<Q>(num_elements);
state.ResumeTiming();
for (int j = 0; j < num_elements; j++) q.pop_front();
benchmark::DoNotOptimize(q);
}
}
BENCHMARK_TEMPLATE(BM_pop_front, Deque<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_pop_front, List<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_pop_front, FwdList<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_pop_front, Chunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_pop_front, ExpChunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_pop_front, Deque<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_pop_front, List<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_pop_front, FwdList<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_pop_front, Chunked<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_pop_front, ExpChunked<Element>)->Apply(CustomArgs);
template <class Q>
void BM_clear(benchmark::State& state) {
const int64_t num_elements = state.range(0);
state.SetItemsProcessed(state.max_iterations * num_elements);
for (auto s : state) {
state.PauseTiming();
Q q = MakeQueue<Q>(num_elements);
state.ResumeTiming();
q.clear();
benchmark::DoNotOptimize(q);
}
}
BENCHMARK_TEMPLATE(BM_clear, Deque<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_clear, List<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_clear, FwdList<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_clear, Chunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_clear, ExpChunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_clear, Deque<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_clear, List<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_clear, FwdList<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_clear, Chunked<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_clear, ExpChunked<Element>)->Apply(CustomArgs);
template <class Q>
void BM_iter(benchmark::State& state) {
const int64_t num_elements = state.range(0);
state.SetItemsProcessed(state.max_iterations * num_elements);
for (auto s : state) {
state.PauseTiming();
Q q = MakeQueue<Q>(state.max_iterations);
int sum = 0;
state.ResumeTiming();
for (const auto& v : q) sum += v;
benchmark::DoNotOptimize(sum);
}
}
BENCHMARK_TEMPLATE(BM_iter, Deque<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_iter, List<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_iter, FwdList<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_iter, Chunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_iter, ExpChunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_iter, Deque<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_iter, List<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_iter, FwdList<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_iter, Chunked<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_iter, ExpChunked<Element>)->Apply(CustomArgs);
template <class Q>
void BM_resize_shrink(benchmark::State& state) {
const int64_t num_elements = state.range(0);
state.SetItemsProcessed(state.max_iterations * num_elements);
for (auto s : state) {
state.PauseTiming();
Q q = MakeQueue<Q>(num_elements * 2);
state.ResumeTiming();
q.resize(num_elements);
benchmark::DoNotOptimize(q);
}
}
// FwdList does not support resize.
BENCHMARK_TEMPLATE(BM_resize_shrink, Deque<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_resize_shrink, List<int64_t>)->Apply(CustomArgs);
// BENCHMARK_TEMPLATE(BM_resize_shrink, FwdList<int64>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_resize_shrink, Chunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_resize_shrink, ExpChunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_resize_shrink, Deque<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_resize_shrink, List<Element>)->Apply(CustomArgs);
// BENCHMARK_TEMPLATE(BM_resize_shrink, FwdList<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_resize_shrink, Chunked<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_resize_shrink, ExpChunked<Element>)->Apply(CustomArgs);
template <class Q>
void BM_resize_grow(benchmark::State& state) {
const int64_t num_elements = state.range(0);
state.SetItemsProcessed(state.max_iterations * num_elements);
for (auto s : state) {
state.PauseTiming();
Q q = MakeQueue<Q>(num_elements);
state.ResumeTiming();
q.resize(static_cast<size_t>(num_elements) * 2);
benchmark::DoNotOptimize(q);
}
}
// FwdList does not support resize.
BENCHMARK_TEMPLATE(BM_resize_grow, Deque<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_resize_grow, List<int64_t>)->Apply(CustomArgs);
// BENCHMARK_TEMPLATE(BM_resize_grow, FwdList<int64>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_resize_grow, Chunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_resize_grow, ExpChunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_resize_grow, Deque<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_resize_grow, List<Element>)->Apply(CustomArgs);
// BENCHMARK_TEMPLATE(BM_resize_grow, FwdList<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_resize_grow, Chunked<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_resize_grow, ExpChunked<Element>)->Apply(CustomArgs);
template <class Q>
void BM_assign_shrink(benchmark::State& state) {
const int64_t num_elements = state.range(0);
state.SetItemsProcessed(state.max_iterations * num_elements);
for (auto s : state) {
state.PauseTiming();
const Q src = MakeQueue<Q>(num_elements);
Q dst = MakeQueue<Q>(num_elements * 2);
state.ResumeTiming();
dst = src;
benchmark::DoNotOptimize(dst);
}
}
BENCHMARK_TEMPLATE(BM_assign_shrink, Deque<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_assign_shrink, List<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_assign_shrink, FwdList<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_assign_shrink, Chunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_assign_shrink, ExpChunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_assign_shrink, Deque<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_assign_shrink, List<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_assign_shrink, FwdList<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_assign_shrink, Chunked<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_assign_shrink, ExpChunked<Element>)->Apply(CustomArgs);
template <class Q>
void BM_assign_grow(benchmark::State& state) {
const int64_t num_elements = state.range(0);
state.SetItemsProcessed(state.max_iterations * num_elements);
for (auto s : state) {
state.PauseTiming();
const Q src = MakeQueue<Q>(num_elements * 2);
Q dst = MakeQueue<Q>(num_elements);
state.ResumeTiming();
dst = src;
benchmark::DoNotOptimize(dst);
}
}
BENCHMARK_TEMPLATE(BM_assign_grow, Deque<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_assign_grow, List<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_assign_grow, FwdList<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_assign_grow, Chunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_assign_grow, ExpChunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_assign_grow, Deque<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_assign_grow, List<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_assign_grow, FwdList<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_assign_grow, Chunked<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_assign_grow, ExpChunked<Element>)->Apply(CustomArgs);
template <class Q>
void BM_push_pop(benchmark::State& state) {
const int64_t num_elements = state.range(0);
state.SetItemsProcessed(state.max_iterations * num_elements);
std::mt19937 rnd;
for (auto s : state) {
state.PauseTiming();
Q q;
state.ResumeTiming();
for (int j = 0; j < num_elements; j++) {
if (q.empty() || absl::Bernoulli(rnd, 0.5)) {
q.push_back(state.iterations());
} else {
q.pop_front();
}
}
benchmark::DoNotOptimize(q);
}
}
BENCHMARK_TEMPLATE(BM_push_pop, Deque<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_push_pop, List<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_push_pop, FwdList<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_push_pop, Chunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_push_pop, ExpChunked<int64_t>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_push_pop, Deque<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_push_pop, List<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_push_pop, FwdList<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_push_pop, Chunked<Element>)->Apply(CustomArgs);
BENCHMARK_TEMPLATE(BM_push_pop, ExpChunked<Element>)->Apply(CustomArgs);
} // namespace