2f76a9bf50
-- 0925eb11f7730d5a1e538b9e6c2d1f8239f5fdc0 by Abseil Team <absl-team@google.com>: Import of CCTZ from GitHub. PiperOrigin-RevId: 250694613 -- 4e1690e492a8399da1b1450ff5f21adf435fcef5 by Greg Falcon <gfalcon@google.com>: Import of CCTZ from GitHub. PiperOrigin-RevId: 250684222 -- c7281b44eb005c21d45bdc0b437e47c7787275bc by Derek Mauro <dmauro@google.com>: Fix warnings on Xcode/clang. PiperOrigin-RevId: 250552120 -- 5ea3b1cfa759a7e6c70e7558f27c762baf818f7d by CJ Johnson <johnsoncj@google.com>: Minor edits to InlinedVector benchmarks: Adds DoNotOptimize to the member functions of NontrivialType, removes unused template parameter for InlVec typeder PiperOrigin-RevId: 250505482 -- 7fe9c02b49121936c5b47034f20272a916111174 by Matt Kulukundis <kfm@google.com>: Internal change. PiperOrigin-RevId: 250376825 -- ad348c9c0eb37449874648e8544c56343c1dfaef by CJ Johnson <johnsoncj@google.com>: Minor edits to InlinedVector benchmark PiperOrigin-RevId: 250361830 -- 55e8b411431e982059af73b160fa6bcef90e87f7 by CJ Johnson <johnsoncj@google.com>: Switches use of allocator_traits to the Abseil backport to leverage existing bug fixes and workarounds PiperOrigin-RevId: 250359321 -- b0edbe3218940128523e36388a31ff90df01a364 by CJ Johnson <johnsoncj@google.com>: Updates exception safety test for InlinedVector to use TYPED_TEST (with fixtures) which increases the coverage without complicating the code. PiperOrigin-RevId: 250359198 -- 8ab55c9a8c191aabcb562cf1789f360eba0b1a81 by Abseil Team <absl-team@google.com>: Internal change. PiperOrigin-RevId: 250281509 -- dd8a67f4f9e5e8930457203c18205183b8306b5a by Abseil Team <absl-team@google.com>: Change the suggestions for the non-strict Next/PrevWeekday() calls. Previously we suggested `PrevWeekday(d, Weekday::thursday) + 7` to get the _following_ Thursday if d is not already a Thursday, but `NextWeekday(d - 1, Weekday::thursday)` is more intuitive, and probably even a little faster. Similarly for the _previous_ Thursday if d is not already a Thursday, suggest `PrevWeekday(d + 1, Weekday::thursday)` instead of `NextWeekday(d, Weekday::thursday) - 7`. PiperOrigin-RevId: 249840844 -- 4775dce26cdb0560011a5d1ecdc8e0c20f856911 by Abseil Team <absl-team@google.com>: Change the DbgHelp lib pragma to use lowercase and an extension. This matches the conventions used in the Windows SDK. PiperOrigin-RevId: 249826229 -- cbc49e865f3715ebe1983307d4f02e50998b2958 by Abseil Team <absl-team@google.com>: Introduce Abseil prefixed thread annotation macros. PiperOrigin-RevId: 249825200 -- 8e97c8dfbadb78743c2421b494398be25f632cb1 by Abseil Team <absl-team@google.com>: Internal change. PiperOrigin-RevId: 249737936 -- 0d720538bc6af3e77ac19def27a4a0514c2800d2 by Abseil Team <absl-team@google.com>: Tell CMake Abseil is a C++ project. PiperOrigin-RevId: 249726509 -- 20651845edf236757b3ffb3eaeff330af81be40a by Abseil Team <absl-team@google.com>: Internal change. PiperOrigin-RevId: 249726377 GitOrigin-RevId: 0925eb11f7730d5a1e538b9e6c2d1f8239f5fdc0 Change-Id: I05d18d30ad4e9ace6b60a17d2dc6fd699643fe30
448 lines
13 KiB
C++
448 lines
13 KiB
C++
// Copyright 2019 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 <array>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "benchmark/benchmark.h"
|
|
#include "absl/base/internal/raw_logging.h"
|
|
#include "absl/base/macros.h"
|
|
#include "absl/container/inlined_vector.h"
|
|
#include "absl/strings/str_cat.h"
|
|
|
|
namespace {
|
|
|
|
void BM_InlinedVectorFill(benchmark::State& state) {
|
|
absl::InlinedVector<int, 8> v;
|
|
int val = 10;
|
|
for (auto _ : state) {
|
|
benchmark::DoNotOptimize(v);
|
|
v.push_back(val);
|
|
}
|
|
}
|
|
BENCHMARK(BM_InlinedVectorFill)->Range(0, 1024);
|
|
|
|
void BM_InlinedVectorFillRange(benchmark::State& state) {
|
|
const int len = state.range(0);
|
|
std::unique_ptr<int[]> ia(new int[len]);
|
|
for (int i = 0; i < len; i++) {
|
|
ia[i] = i;
|
|
}
|
|
auto* from = ia.get();
|
|
auto* to = from + len;
|
|
for (auto _ : state) {
|
|
benchmark::DoNotOptimize(from);
|
|
benchmark::DoNotOptimize(to);
|
|
absl::InlinedVector<int, 8> v(from, to);
|
|
benchmark::DoNotOptimize(v);
|
|
}
|
|
}
|
|
BENCHMARK(BM_InlinedVectorFillRange)->Range(0, 1024);
|
|
|
|
void BM_StdVectorFill(benchmark::State& state) {
|
|
std::vector<int> v;
|
|
int val = 10;
|
|
for (auto _ : state) {
|
|
benchmark::DoNotOptimize(v);
|
|
benchmark::DoNotOptimize(val);
|
|
v.push_back(val);
|
|
}
|
|
}
|
|
BENCHMARK(BM_StdVectorFill)->Range(0, 1024);
|
|
|
|
// The purpose of the next two benchmarks is to verify that
|
|
// absl::InlinedVector is efficient when moving is more efficent than
|
|
// copying. To do so, we use strings that are larger than the short
|
|
// string optimization.
|
|
bool StringRepresentedInline(std::string s) {
|
|
const char* chars = s.data();
|
|
std::string s1 = std::move(s);
|
|
return s1.data() != chars;
|
|
}
|
|
|
|
int GetNonShortStringOptimizationSize() {
|
|
for (int i = 24; i <= 192; i *= 2) {
|
|
if (!StringRepresentedInline(std::string(i, 'A'))) {
|
|
return i;
|
|
}
|
|
}
|
|
ABSL_RAW_LOG(
|
|
FATAL,
|
|
"Failed to find a std::string larger than the short std::string optimization");
|
|
return -1;
|
|
}
|
|
|
|
void BM_InlinedVectorFillString(benchmark::State& state) {
|
|
const int len = state.range(0);
|
|
const int no_sso = GetNonShortStringOptimizationSize();
|
|
std::string strings[4] = {std::string(no_sso, 'A'), std::string(no_sso, 'B'),
|
|
std::string(no_sso, 'C'), std::string(no_sso, 'D')};
|
|
|
|
for (auto _ : state) {
|
|
absl::InlinedVector<std::string, 8> v;
|
|
for (int i = 0; i < len; i++) {
|
|
v.push_back(strings[i & 3]);
|
|
}
|
|
}
|
|
state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len);
|
|
}
|
|
BENCHMARK(BM_InlinedVectorFillString)->Range(0, 1024);
|
|
|
|
void BM_StdVectorFillString(benchmark::State& state) {
|
|
const int len = state.range(0);
|
|
const int no_sso = GetNonShortStringOptimizationSize();
|
|
std::string strings[4] = {std::string(no_sso, 'A'), std::string(no_sso, 'B'),
|
|
std::string(no_sso, 'C'), std::string(no_sso, 'D')};
|
|
|
|
for (auto _ : state) {
|
|
std::vector<std::string> v;
|
|
for (int i = 0; i < len; i++) {
|
|
v.push_back(strings[i & 3]);
|
|
}
|
|
}
|
|
state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len);
|
|
}
|
|
BENCHMARK(BM_StdVectorFillString)->Range(0, 1024);
|
|
|
|
struct Buffer { // some arbitrary structure for benchmarking.
|
|
char* base;
|
|
int length;
|
|
int capacity;
|
|
void* user_data;
|
|
};
|
|
|
|
void BM_InlinedVectorAssignments(benchmark::State& state) {
|
|
const int len = state.range(0);
|
|
using BufferVec = absl::InlinedVector<Buffer, 2>;
|
|
|
|
BufferVec src;
|
|
src.resize(len);
|
|
|
|
BufferVec dst;
|
|
for (auto _ : state) {
|
|
benchmark::DoNotOptimize(dst);
|
|
benchmark::DoNotOptimize(src);
|
|
dst = src;
|
|
}
|
|
}
|
|
BENCHMARK(BM_InlinedVectorAssignments)
|
|
->Arg(0)
|
|
->Arg(1)
|
|
->Arg(2)
|
|
->Arg(3)
|
|
->Arg(4)
|
|
->Arg(20);
|
|
|
|
void BM_CreateFromContainer(benchmark::State& state) {
|
|
for (auto _ : state) {
|
|
absl::InlinedVector<int, 4> src{1, 2, 3};
|
|
benchmark::DoNotOptimize(src);
|
|
absl::InlinedVector<int, 4> dst(std::move(src));
|
|
benchmark::DoNotOptimize(dst);
|
|
}
|
|
}
|
|
BENCHMARK(BM_CreateFromContainer);
|
|
|
|
struct LargeCopyableOnly {
|
|
LargeCopyableOnly() : d(1024, 17) {}
|
|
LargeCopyableOnly(const LargeCopyableOnly& o) = default;
|
|
LargeCopyableOnly& operator=(const LargeCopyableOnly& o) = default;
|
|
|
|
std::vector<int> d;
|
|
};
|
|
|
|
struct LargeCopyableSwappable {
|
|
LargeCopyableSwappable() : d(1024, 17) {}
|
|
|
|
LargeCopyableSwappable(const LargeCopyableSwappable& o) = default;
|
|
|
|
LargeCopyableSwappable& operator=(LargeCopyableSwappable o) {
|
|
using std::swap;
|
|
swap(*this, o);
|
|
return *this;
|
|
}
|
|
|
|
friend void swap(LargeCopyableSwappable& a, LargeCopyableSwappable& b) {
|
|
using std::swap;
|
|
swap(a.d, b.d);
|
|
}
|
|
|
|
std::vector<int> d;
|
|
};
|
|
|
|
struct LargeCopyableMovable {
|
|
LargeCopyableMovable() : d(1024, 17) {}
|
|
// Use implicitly defined copy and move.
|
|
|
|
std::vector<int> d;
|
|
};
|
|
|
|
struct LargeCopyableMovableSwappable {
|
|
LargeCopyableMovableSwappable() : d(1024, 17) {}
|
|
LargeCopyableMovableSwappable(const LargeCopyableMovableSwappable& o) =
|
|
default;
|
|
LargeCopyableMovableSwappable(LargeCopyableMovableSwappable&& o) = default;
|
|
|
|
LargeCopyableMovableSwappable& operator=(LargeCopyableMovableSwappable o) {
|
|
using std::swap;
|
|
swap(*this, o);
|
|
return *this;
|
|
}
|
|
LargeCopyableMovableSwappable& operator=(LargeCopyableMovableSwappable&& o) =
|
|
default;
|
|
|
|
friend void swap(LargeCopyableMovableSwappable& a,
|
|
LargeCopyableMovableSwappable& b) {
|
|
using std::swap;
|
|
swap(a.d, b.d);
|
|
}
|
|
|
|
std::vector<int> d;
|
|
};
|
|
|
|
template <typename ElementType>
|
|
void BM_SwapElements(benchmark::State& state) {
|
|
const int len = state.range(0);
|
|
using Vec = absl::InlinedVector<ElementType, 32>;
|
|
Vec a(len);
|
|
Vec b;
|
|
for (auto _ : state) {
|
|
using std::swap;
|
|
benchmark::DoNotOptimize(a);
|
|
benchmark::DoNotOptimize(b);
|
|
swap(a, b);
|
|
}
|
|
}
|
|
BENCHMARK_TEMPLATE(BM_SwapElements, LargeCopyableOnly)->Range(0, 1024);
|
|
BENCHMARK_TEMPLATE(BM_SwapElements, LargeCopyableSwappable)->Range(0, 1024);
|
|
BENCHMARK_TEMPLATE(BM_SwapElements, LargeCopyableMovable)->Range(0, 1024);
|
|
BENCHMARK_TEMPLATE(BM_SwapElements, LargeCopyableMovableSwappable)
|
|
->Range(0, 1024);
|
|
|
|
// The following benchmark is meant to track the efficiency of the vector size
|
|
// as a function of stored type via the benchmark label. It is not meant to
|
|
// output useful sizeof operator performance. The loop is a dummy operation
|
|
// to fulfill the requirement of running the benchmark.
|
|
template <typename VecType>
|
|
void BM_Sizeof(benchmark::State& state) {
|
|
int size = 0;
|
|
for (auto _ : state) {
|
|
VecType vec;
|
|
size = sizeof(vec);
|
|
}
|
|
state.SetLabel(absl::StrCat("sz=", size));
|
|
}
|
|
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<char, 1>);
|
|
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<char, 4>);
|
|
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<char, 7>);
|
|
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<char, 8>);
|
|
|
|
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<int, 1>);
|
|
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<int, 4>);
|
|
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<int, 7>);
|
|
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<int, 8>);
|
|
|
|
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<void*, 1>);
|
|
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<void*, 4>);
|
|
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<void*, 7>);
|
|
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<void*, 8>);
|
|
|
|
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<std::string, 1>);
|
|
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<std::string, 4>);
|
|
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<std::string, 7>);
|
|
BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<std::string, 8>);
|
|
|
|
void BM_InlinedVectorIndexInlined(benchmark::State& state) {
|
|
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
|
|
for (auto _ : state) {
|
|
benchmark::DoNotOptimize(v);
|
|
benchmark::DoNotOptimize(v[4]);
|
|
}
|
|
}
|
|
BENCHMARK(BM_InlinedVectorIndexInlined);
|
|
|
|
void BM_InlinedVectorIndexExternal(benchmark::State& state) {
|
|
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
|
for (auto _ : state) {
|
|
benchmark::DoNotOptimize(v);
|
|
benchmark::DoNotOptimize(v[4]);
|
|
}
|
|
}
|
|
BENCHMARK(BM_InlinedVectorIndexExternal);
|
|
|
|
void BM_StdVectorIndex(benchmark::State& state) {
|
|
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
|
for (auto _ : state) {
|
|
benchmark::DoNotOptimize(v);
|
|
benchmark::DoNotOptimize(v[4]);
|
|
}
|
|
}
|
|
BENCHMARK(BM_StdVectorIndex);
|
|
|
|
void BM_InlinedVectorDataInlined(benchmark::State& state) {
|
|
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
|
|
for (auto _ : state) {
|
|
benchmark::DoNotOptimize(v);
|
|
benchmark::DoNotOptimize(v.data());
|
|
}
|
|
}
|
|
BENCHMARK(BM_InlinedVectorDataInlined);
|
|
|
|
void BM_InlinedVectorDataExternal(benchmark::State& state) {
|
|
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
|
for (auto _ : state) {
|
|
benchmark::DoNotOptimize(v);
|
|
benchmark::DoNotOptimize(v.data());
|
|
}
|
|
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
|
|
}
|
|
BENCHMARK(BM_InlinedVectorDataExternal);
|
|
|
|
void BM_StdVectorData(benchmark::State& state) {
|
|
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
|
for (auto _ : state) {
|
|
benchmark::DoNotOptimize(v);
|
|
benchmark::DoNotOptimize(v.data());
|
|
}
|
|
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations()));
|
|
}
|
|
BENCHMARK(BM_StdVectorData);
|
|
|
|
void BM_InlinedVectorSizeInlined(benchmark::State& state) {
|
|
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
|
|
for (auto _ : state) {
|
|
benchmark::DoNotOptimize(v);
|
|
benchmark::DoNotOptimize(v.size());
|
|
}
|
|
}
|
|
BENCHMARK(BM_InlinedVectorSizeInlined);
|
|
|
|
void BM_InlinedVectorSizeExternal(benchmark::State& state) {
|
|
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
|
for (auto _ : state) {
|
|
benchmark::DoNotOptimize(v);
|
|
benchmark::DoNotOptimize(v.size());
|
|
}
|
|
}
|
|
BENCHMARK(BM_InlinedVectorSizeExternal);
|
|
|
|
void BM_StdVectorSize(benchmark::State& state) {
|
|
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
|
for (auto _ : state) {
|
|
benchmark::DoNotOptimize(v);
|
|
benchmark::DoNotOptimize(v.size());
|
|
}
|
|
}
|
|
BENCHMARK(BM_StdVectorSize);
|
|
|
|
void BM_InlinedVectorEmptyInlined(benchmark::State& state) {
|
|
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7};
|
|
for (auto _ : state) {
|
|
benchmark::DoNotOptimize(v);
|
|
benchmark::DoNotOptimize(v.empty());
|
|
}
|
|
}
|
|
BENCHMARK(BM_InlinedVectorEmptyInlined);
|
|
|
|
void BM_InlinedVectorEmptyExternal(benchmark::State& state) {
|
|
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
|
for (auto _ : state) {
|
|
benchmark::DoNotOptimize(v);
|
|
benchmark::DoNotOptimize(v.empty());
|
|
}
|
|
}
|
|
BENCHMARK(BM_InlinedVectorEmptyExternal);
|
|
|
|
void BM_StdVectorEmpty(benchmark::State& state) {
|
|
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
|
for (auto _ : state) {
|
|
benchmark::DoNotOptimize(v);
|
|
benchmark::DoNotOptimize(v.empty());
|
|
}
|
|
}
|
|
BENCHMARK(BM_StdVectorEmpty);
|
|
|
|
constexpr size_t kInlinedCapacity = 4;
|
|
constexpr size_t kLargeSize = kInlinedCapacity * 2;
|
|
constexpr size_t kSmallSize = kInlinedCapacity / 2;
|
|
constexpr size_t kBatchSize = 100;
|
|
|
|
template <typename T>
|
|
using InlVec = absl::InlinedVector<T, kInlinedCapacity>;
|
|
|
|
struct TrivialType {
|
|
size_t val;
|
|
};
|
|
|
|
class NontrivialType {
|
|
public:
|
|
ABSL_ATTRIBUTE_NOINLINE NontrivialType() : val_() {
|
|
benchmark::DoNotOptimize(*this);
|
|
}
|
|
|
|
ABSL_ATTRIBUTE_NOINLINE NontrivialType(const NontrivialType& other)
|
|
: val_(other.val_) {
|
|
benchmark::DoNotOptimize(*this);
|
|
}
|
|
|
|
ABSL_ATTRIBUTE_NOINLINE NontrivialType& operator=(
|
|
const NontrivialType& other) {
|
|
val_ = other.val_;
|
|
benchmark::DoNotOptimize(*this);
|
|
return *this;
|
|
}
|
|
|
|
ABSL_ATTRIBUTE_NOINLINE ~NontrivialType() noexcept {
|
|
benchmark::DoNotOptimize(*this);
|
|
}
|
|
|
|
private:
|
|
size_t val_;
|
|
};
|
|
|
|
template <typename T, typename PrepareVecFn, typename TestVecFn>
|
|
void BatchedBenchmark(benchmark::State& state, PrepareVecFn prepare_vec,
|
|
TestVecFn test_vec) {
|
|
std::array<InlVec<T>, kBatchSize> vector_batch{};
|
|
|
|
while (state.KeepRunningBatch(kBatchSize)) {
|
|
// Prepare batch
|
|
state.PauseTiming();
|
|
for (auto& vec : vector_batch) {
|
|
prepare_vec(&vec);
|
|
}
|
|
benchmark::DoNotOptimize(vector_batch);
|
|
state.ResumeTiming();
|
|
|
|
// Test batch
|
|
for (auto& vec : vector_batch) {
|
|
test_vec(&vec);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename T, size_t FromSize>
|
|
void BM_Clear(benchmark::State& state) {
|
|
BatchedBenchmark<T>(
|
|
state,
|
|
/* prepare_vec = */ [](InlVec<T>* vec) { vec->resize(FromSize); },
|
|
/* test_vec = */ [](InlVec<T>* vec) { vec->clear(); });
|
|
}
|
|
BENCHMARK_TEMPLATE(BM_Clear, TrivialType, kLargeSize);
|
|
BENCHMARK_TEMPLATE(BM_Clear, TrivialType, kSmallSize);
|
|
BENCHMARK_TEMPLATE(BM_Clear, NontrivialType, kLargeSize);
|
|
BENCHMARK_TEMPLATE(BM_Clear, NontrivialType, kSmallSize);
|
|
|
|
} // namespace
|