Abseil Common Libraries (C++) (grcp 依赖)
https://abseil.io/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
538 lines
16 KiB
538 lines
16 KiB
// 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; |
|
|
|
#define ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_FunctionTemplate, T) \ |
|
BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kLargeSize); \ |
|
BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kSmallSize) |
|
|
|
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 (size_t i = 0; i < kBatchSize; ++i) { |
|
prepare_vec(vector_batch.data() + i, i); |
|
} |
|
benchmark::DoNotOptimize(vector_batch); |
|
state.ResumeTiming(); |
|
|
|
// Test batch |
|
for (size_t i = 0; i < kBatchSize; ++i) { |
|
test_vec(vector_batch.data() + i, i); |
|
} |
|
} |
|
} |
|
|
|
template <typename T, size_t ToSize> |
|
void BM_ConstructFromSize(benchmark::State& state) { |
|
using VecT = InlVec<T>; |
|
auto size = ToSize; |
|
BatchedBenchmark<T>( |
|
state, |
|
/* prepare_vec = */ [](InlVec<T>* vec, size_t) { vec->~VecT(); }, |
|
/* test_vec = */ |
|
[&](void* ptr, size_t) { |
|
benchmark::DoNotOptimize(size); |
|
::new (ptr) VecT(size); |
|
}); |
|
} |
|
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromSize, TrivialType); |
|
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromSize, NontrivialType); |
|
|
|
template <typename T, size_t ToSize> |
|
void BM_ConstructFromSizeRef(benchmark::State& state) { |
|
using VecT = InlVec<T>; |
|
auto size = ToSize; |
|
auto ref = T(); |
|
BatchedBenchmark<T>( |
|
state, |
|
/* prepare_vec = */ [](InlVec<T>* vec, size_t) { vec->~VecT(); }, |
|
/* test_vec = */ |
|
[&](void* ptr, size_t) { |
|
benchmark::DoNotOptimize(size); |
|
benchmark::DoNotOptimize(ref); |
|
::new (ptr) VecT(size, ref); |
|
}); |
|
} |
|
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromSizeRef, TrivialType); |
|
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromSizeRef, NontrivialType); |
|
|
|
template <typename T, size_t ToSize> |
|
void BM_ConstructFromRange(benchmark::State& state) { |
|
using VecT = InlVec<T>; |
|
std::array<T, ToSize> arr{}; |
|
BatchedBenchmark<T>( |
|
state, |
|
/* prepare_vec = */ [](InlVec<T>* vec, size_t) { vec->~VecT(); }, |
|
/* test_vec = */ |
|
[&](void* ptr, size_t) { |
|
benchmark::DoNotOptimize(arr); |
|
::new (ptr) VecT(arr.begin(), arr.end()); |
|
}); |
|
} |
|
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromRange, TrivialType); |
|
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromRange, NontrivialType); |
|
|
|
template <typename T, size_t ToSize> |
|
void BM_ConstructFromCopy(benchmark::State& state) { |
|
using VecT = InlVec<T>; |
|
VecT other_vec(ToSize); |
|
BatchedBenchmark<T>( |
|
state, |
|
/* prepare_vec = */ |
|
[](InlVec<T>* vec, size_t) { vec->~VecT(); }, |
|
/* test_vec = */ |
|
[&](void* ptr, size_t) { |
|
benchmark::DoNotOptimize(other_vec); |
|
::new (ptr) VecT(other_vec); |
|
}); |
|
} |
|
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromCopy, TrivialType); |
|
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromCopy, NontrivialType); |
|
|
|
template <typename T, size_t ToSize> |
|
void BM_ConstructFromMove(benchmark::State& state) { |
|
using VecT = InlVec<T>; |
|
std::array<VecT, kBatchSize> vector_batch{}; |
|
BatchedBenchmark<T>( |
|
state, |
|
/* prepare_vec = */ |
|
[&](InlVec<T>* vec, size_t i) { |
|
vector_batch[i].clear(); |
|
vector_batch[i].resize(ToSize); |
|
vec->~VecT(); |
|
}, |
|
/* test_vec = */ |
|
[&](void* ptr, size_t i) { |
|
benchmark::DoNotOptimize(vector_batch[i]); |
|
::new (ptr) VecT(std::move(vector_batch[i])); |
|
}); |
|
} |
|
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromMove, TrivialType); |
|
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromMove, NontrivialType); |
|
|
|
template <typename T, size_t FromSize> |
|
void BM_Clear(benchmark::State& state) { |
|
BatchedBenchmark<T>( |
|
state, |
|
/* prepare_vec = */ [](InlVec<T>* vec, size_t) { vec->resize(FromSize); }, |
|
/* test_vec = */ [](InlVec<T>* vec, size_t) { vec->clear(); }); |
|
} |
|
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_Clear, TrivialType); |
|
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_Clear, NontrivialType); |
|
|
|
} // namespace
|
|
|