- 07191b0f52301e1e4a790e236f7b7c2fd90561ae Disambiguates computed return type of absl::optional logi... by Abseil Team <absl-team@google.com>
- acd95f8ec4e6ec1587cb198c7f40af3c81094d92 Release container benchmarks. by Alex Strelnikov <strel@google.com> - 80f596b6b7c5e06453e778c16527d5a0e85f8413 Allow absl::base_internal::AtomicHook to have a default v... by Derek Mauro <dmauro@google.com> - 8402631546af8bcbd4acdf897d0cdfb805ad544a Release thread_identity benchmark. by Alex Strelnikov <strel@google.com> - 6dcb1e90fefb8556ce4654983d3a73c7585b4b99 Fix spelling error in variant.h by Abseil Team <absl-team@google.com> - faa8a81e1442018c0d400b09a595a5be55074715 Run tests from CMake. The CI is currently Linux only, fo... by Jon Cohen <cohenjon@google.com> - 745ed6db574f931f2ec3a88e964fb03a5f22f816 Internal change. by Derek Mauro <dmauro@google.com> - 23facd7d1c5f43ac8181b016ee4acc5955f048c1 absl::variant exception safety test. by Xiaoyi Zhang <zhangxy@google.com> - c18e21e7cf8f6e83ae9d90e536e886409dd6cf68 Reinstate the syntax check on time-zone abbreviations now... by Abseil Team <absl-team@google.com> - da469f4314f0c820665a2b5b9477af9462b23e42 Import CCTZ changes to internal copy. by Shaindel Schwartz <shaindel@google.com> - 44ea35843517be03ab256b69449ccfea64352621 Import CCTZ changes to internal copy. by Abseil Team <absl-team@google.com> - 55d1105312687c6093950fac831c7540f49045b5 Import CCTZ changes to internal copy. by Greg Falcon <gfalcon@google.com> - 58d7965ad274406410b6d833213eca04d41c6867 Add zoneinfo as a data dependency to the //absl/time tests. by Shaindel Schwartz <shaindel@google.com> - 6acc50146f9ff29015bfaaa5bf9900691f839da5 Change benchmark target type from cc_test to cc_binary. by Alex Strelnikov <strel@google.com> - db3fbdae8f9f285a466f7a070326b1ce43b6a0dd Update WORKSPACE for C++ microbenchmarks and release algo... by Alex Strelnikov <strel@google.com> - 0869ae168255242af651853ed01719166d8cebf6 Update to Bazel version 0.13.0. by Abseil Team <absl-team@google.com> - e507dd53ab788964207fdf27d31b72a33c296fab Add missing include of cstdio by Abseil Team <absl-team@google.com> GitOrigin-RevId: 07191b0f52301e1e4a790e236f7b7c2fd90561ae Change-Id: I90994cf2b438fbec894724dcd9b90882281eef56pull/117/head
parent
9613678332
commit
26b789f9a5
35 changed files with 1583 additions and 119 deletions
@ -0,0 +1,15 @@ |
||||
cmake_minimum_required(VERSION 2.8.2) |
||||
|
||||
project(googletest-download NONE) |
||||
|
||||
include(ExternalProject) |
||||
ExternalProject_Add(googletest |
||||
GIT_REPOSITORY https://github.com/google/googletest.git |
||||
GIT_TAG master |
||||
SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" |
||||
BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" |
||||
CONFIGURE_COMMAND "" |
||||
BUILD_COMMAND "" |
||||
INSTALL_COMMAND "" |
||||
TEST_COMMAND "" |
||||
) |
@ -0,0 +1,32 @@ |
||||
# Downloads and unpacks googletest at configure time. Based on the instructions |
||||
# at https://github.com/google/googletest/tree/master/googletest#incorporating-into-an-existing-cmake-project |
||||
|
||||
# Download the latest googletest from Github master |
||||
configure_file( |
||||
${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt.in |
||||
googletest-download/CMakeLists.txt |
||||
) |
||||
|
||||
# Configure and build the downloaded googletest source |
||||
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . |
||||
RESULT_VARIABLE result |
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) |
||||
if(result) |
||||
message(FATAL_ERROR "CMake step for googletest failed: ${result}") |
||||
endif() |
||||
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} --build . |
||||
RESULT_VARIABLE result |
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download) |
||||
if(result) |
||||
message(FATAL_ERROR "Build step for googletest failed: ${result}") |
||||
endif() |
||||
|
||||
# Prevent overriding the parent project's compiler/linker settings on Windows |
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) |
||||
|
||||
# Add googletest directly to our build. This defines the gtest and gtest_main |
||||
# targets. |
||||
add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src |
||||
${CMAKE_BINARY_DIR}/googletest-build |
||||
EXCLUDE_FROM_ALL) |
@ -0,0 +1,128 @@ |
||||
// Copyright 2017 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
|
||||
//
|
||||
// http://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 <cstdint> |
||||
#include <cstring> |
||||
|
||||
#include "absl/algorithm/algorithm.h" |
||||
#include "benchmark/benchmark.h" |
||||
|
||||
namespace { |
||||
|
||||
// The range of sequence sizes to benchmark.
|
||||
constexpr int kMinBenchmarkSize = 1024; |
||||
constexpr int kMaxBenchmarkSize = 8 * 1024 * 1024; |
||||
|
||||
// A user-defined type for use in equality benchmarks. Note that we expect
|
||||
// std::memcmp to win for this type: libstdc++'s std::equal only defers to
|
||||
// memcmp for integral types. This is because it is not straightforward to
|
||||
// guarantee that std::memcmp would produce a result "as-if" compared by
|
||||
// operator== for other types (example gotchas: NaN floats, structs with
|
||||
// padding).
|
||||
struct EightBits { |
||||
explicit EightBits(int /* unused */) : data(0) {} |
||||
bool operator==(const EightBits& rhs) const { return data == rhs.data; } |
||||
uint8_t data; |
||||
}; |
||||
|
||||
template <typename T> |
||||
void BM_absl_equal_benchmark(benchmark::State& state) { |
||||
std::vector<T> xs(state.range(0), T(0)); |
||||
std::vector<T> ys = xs; |
||||
while (state.KeepRunning()) { |
||||
const bool same = absl::equal(xs.begin(), xs.end(), ys.begin(), ys.end()); |
||||
benchmark::DoNotOptimize(same); |
||||
} |
||||
} |
||||
|
||||
template <typename T> |
||||
void BM_std_equal_benchmark(benchmark::State& state) { |
||||
std::vector<T> xs(state.range(0), T(0)); |
||||
std::vector<T> ys = xs; |
||||
while (state.KeepRunning()) { |
||||
const bool same = std::equal(xs.begin(), xs.end(), ys.begin()); |
||||
benchmark::DoNotOptimize(same); |
||||
} |
||||
} |
||||
|
||||
template <typename T> |
||||
void BM_memcmp_benchmark(benchmark::State& state) { |
||||
std::vector<T> xs(state.range(0), T(0)); |
||||
std::vector<T> ys = xs; |
||||
while (state.KeepRunning()) { |
||||
const bool same = |
||||
std::memcmp(xs.data(), ys.data(), xs.size() * sizeof(T)) == 0; |
||||
benchmark::DoNotOptimize(same); |
||||
} |
||||
} |
||||
|
||||
// The expectation is that the compiler should be able to elide the equality
|
||||
// comparison altogether for sufficiently simple types.
|
||||
template <typename T> |
||||
void BM_absl_equal_self_benchmark(benchmark::State& state) { |
||||
std::vector<T> xs(state.range(0), T(0)); |
||||
while (state.KeepRunning()) { |
||||
const bool same = absl::equal(xs.begin(), xs.end(), xs.begin(), xs.end()); |
||||
benchmark::DoNotOptimize(same); |
||||
} |
||||
} |
||||
|
||||
BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint8_t) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint8_t) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint8_t) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint8_t) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
|
||||
BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint16_t) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint16_t) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint16_t) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint16_t) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
|
||||
BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint32_t) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint32_t) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint32_t) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint32_t) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
|
||||
BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint64_t) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint64_t) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint64_t) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint64_t) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
|
||||
BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, EightBits) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
BENCHMARK_TEMPLATE(BM_std_equal_benchmark, EightBits) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
BENCHMARK_TEMPLATE(BM_memcmp_benchmark, EightBits) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, EightBits) |
||||
->Range(kMinBenchmarkSize, kMaxBenchmarkSize); |
||||
|
||||
} // namespace
|
||||
|
||||
BENCHMARK_MAIN(); |
@ -0,0 +1,70 @@ |
||||
// Copyright 2018 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
|
||||
//
|
||||
// http://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 "absl/base/internal/atomic_hook.h" |
||||
|
||||
#include "gtest/gtest.h" |
||||
#include "absl/base/attributes.h" |
||||
|
||||
namespace { |
||||
|
||||
int value = 0; |
||||
void TestHook(int x) { value = x; } |
||||
|
||||
TEST(AtomicHookTest, NoDefaultFunction) { |
||||
ABSL_CONST_INIT static absl::base_internal::AtomicHook<void(*)(int)> hook; |
||||
value = 0; |
||||
|
||||
// Test the default DummyFunction.
|
||||
EXPECT_TRUE(hook.Load() == nullptr); |
||||
EXPECT_EQ(value, 0); |
||||
hook(1); |
||||
EXPECT_EQ(value, 0); |
||||
|
||||
// Test a stored hook.
|
||||
hook.Store(TestHook); |
||||
EXPECT_TRUE(hook.Load() == TestHook); |
||||
EXPECT_EQ(value, 0); |
||||
hook(1); |
||||
EXPECT_EQ(value, 1); |
||||
|
||||
// Calling Store() with the same hook should not crash.
|
||||
hook.Store(TestHook); |
||||
EXPECT_TRUE(hook.Load() == TestHook); |
||||
EXPECT_EQ(value, 1); |
||||
hook(2); |
||||
EXPECT_EQ(value, 2); |
||||
} |
||||
|
||||
TEST(AtomicHookTest, WithDefaultFunction) { |
||||
// Set the default value to TestHook at compile-time.
|
||||
ABSL_CONST_INIT static absl::base_internal::AtomicHook<void (*)(int)> hook( |
||||
TestHook); |
||||
value = 0; |
||||
|
||||
// Test the default value is TestHook.
|
||||
EXPECT_TRUE(hook.Load() == TestHook); |
||||
EXPECT_EQ(value, 0); |
||||
hook(1); |
||||
EXPECT_EQ(value, 1); |
||||
|
||||
// Calling Store() with the same hook should not crash.
|
||||
hook.Store(TestHook); |
||||
EXPECT_TRUE(hook.Load() == TestHook); |
||||
EXPECT_EQ(value, 1); |
||||
hook(2); |
||||
EXPECT_EQ(value, 2); |
||||
} |
||||
|
||||
} // namespace
|
@ -0,0 +1,40 @@ |
||||
// Copyright 2017 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
|
||||
//
|
||||
// http://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 "absl/base/internal/thread_identity.h" |
||||
#include "absl/synchronization/internal/create_thread_identity.h" |
||||
#include "absl/synchronization/internal/per_thread_sem.h" |
||||
#include "benchmark/benchmark.h" |
||||
|
||||
namespace { |
||||
|
||||
void BM_SafeCurrentThreadIdentity(benchmark::State& state) { |
||||
for (auto _ : state) { |
||||
benchmark::DoNotOptimize( |
||||
absl::synchronization_internal::GetOrCreateCurrentThreadIdentity()); |
||||
} |
||||
} |
||||
BENCHMARK(BM_SafeCurrentThreadIdentity); |
||||
|
||||
void BM_UnsafeCurrentThreadIdentity(benchmark::State& state) { |
||||
for (auto _ : state) { |
||||
benchmark::DoNotOptimize( |
||||
absl::base_internal::CurrentThreadIdentityIfPresent()); |
||||
} |
||||
} |
||||
BENCHMARK(BM_UnsafeCurrentThreadIdentity); |
||||
|
||||
} // namespace
|
||||
|
||||
BENCHMARK_MAIN(); |
@ -0,0 +1,68 @@ |
||||
// Copyright 2017 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
|
||||
//
|
||||
// http://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 "absl/container/fixed_array.h" |
||||
|
||||
#include <stddef.h> |
||||
#include <string> |
||||
|
||||
#include "benchmark/benchmark.h" |
||||
|
||||
namespace { |
||||
|
||||
// For benchmarking -- simple class with constructor and destructor that
|
||||
// set an int to a constant..
|
||||
class SimpleClass { |
||||
public: |
||||
SimpleClass() : i(3) { } |
||||
~SimpleClass() { i = 0; } |
||||
private: |
||||
int i; |
||||
}; |
||||
|
||||
template <typename C, size_t stack_size> |
||||
void BM_FixedArray(benchmark::State& state) { |
||||
const int size = state.range(0); |
||||
for (auto _ : state) { |
||||
absl::FixedArray<C, stack_size> fa(size); |
||||
benchmark::DoNotOptimize(fa.data()); |
||||
} |
||||
} |
||||
BENCHMARK_TEMPLATE(BM_FixedArray, char, absl::kFixedArrayUseDefault) |
||||
->Range(0, 1 << 16); |
||||
BENCHMARK_TEMPLATE(BM_FixedArray, char, 0)->Range(0, 1 << 16); |
||||
BENCHMARK_TEMPLATE(BM_FixedArray, char, 1)->Range(0, 1 << 16); |
||||
BENCHMARK_TEMPLATE(BM_FixedArray, char, 16)->Range(0, 1 << 16); |
||||
BENCHMARK_TEMPLATE(BM_FixedArray, char, 256)->Range(0, 1 << 16); |
||||
BENCHMARK_TEMPLATE(BM_FixedArray, char, 65536)->Range(0, 1 << 16); |
||||
|
||||
BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, absl::kFixedArrayUseDefault) |
||||
->Range(0, 1 << 16); |
||||
BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 0)->Range(0, 1 << 16); |
||||
BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 1)->Range(0, 1 << 16); |
||||
BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 16)->Range(0, 1 << 16); |
||||
BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 256)->Range(0, 1 << 16); |
||||
BENCHMARK_TEMPLATE(BM_FixedArray, SimpleClass, 65536)->Range(0, 1 << 16); |
||||
|
||||
BENCHMARK_TEMPLATE(BM_FixedArray, std::string, absl::kFixedArrayUseDefault) |
||||
->Range(0, 1 << 16); |
||||
BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 0)->Range(0, 1 << 16); |
||||
BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 1)->Range(0, 1 << 16); |
||||
BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 16)->Range(0, 1 << 16); |
||||
BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 256)->Range(0, 1 << 16); |
||||
BENCHMARK_TEMPLATE(BM_FixedArray, std::string, 65536)->Range(0, 1 << 16); |
||||
|
||||
} // namespace
|
||||
|
||||
BENCHMARK_MAIN(); |
@ -0,0 +1,376 @@ |
||||
// Copyright 2017 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
|
||||
//
|
||||
// http://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 "absl/container/inlined_vector.h" |
||||
|
||||
#include <string> |
||||
#include <vector> |
||||
|
||||
#include "absl/base/internal/raw_logging.h" |
||||
#include "absl/strings/str_cat.h" |
||||
#include "benchmark/benchmark.h" |
||||
|
||||
namespace { |
||||
|
||||
using IntVec = absl::InlinedVector<int, 8>; |
||||
|
||||
void BM_InlinedVectorFill(benchmark::State& state) { |
||||
const int len = state.range(0); |
||||
for (auto _ : state) { |
||||
IntVec v; |
||||
for (int i = 0; i < len; i++) { |
||||
v.push_back(i); |
||||
} |
||||
} |
||||
state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len); |
||||
} |
||||
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; |
||||
} |
||||
for (auto _ : state) { |
||||
IntVec v(ia.get(), ia.get() + len); |
||||
benchmark::DoNotOptimize(v); |
||||
} |
||||
state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len); |
||||
} |
||||
BENCHMARK(BM_InlinedVectorFillRange)->Range(0, 1024); |
||||
|
||||
void BM_StdVectorFill(benchmark::State& state) { |
||||
const int len = state.range(0); |
||||
for (auto _ : state) { |
||||
std::vector<int> v; |
||||
for (int i = 0; i < len; i++) { |
||||
v.push_back(i); |
||||
} |
||||
} |
||||
state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len); |
||||
} |
||||
BENCHMARK(BM_StdVectorFill)->Range(0, 1024); |
||||
|
||||
bool StringRepresentedInline(std::string s) { |
||||
const char* chars = s.data(); |
||||
std::string s1 = std::move(s); |
||||
return s1.data() != chars; |
||||
} |
||||
|
||||
void BM_InlinedVectorFillString(benchmark::State& state) { |
||||
const int len = state.range(0); |
||||
std::string strings[4] = {"a quite long string", |
||||
"another long string", |
||||
"012345678901234567", |
||||
"to cause allocation"}; |
||||
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); |
||||
std::string strings[4] = {"a quite long string", |
||||
"another long string", |
||||
"012345678901234567", |
||||
"to cause allocation"}; |
||||
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); |
||||
// The purpose of the benchmark is to verify that inlined vector is
|
||||
// efficient when moving is more efficent than copying. To do so, we
|
||||
// use strings that are larger than the small std::string optimization.
|
||||
ABSL_RAW_CHECK(!StringRepresentedInline(strings[0]), |
||||
"benchmarked with strings that are too small"); |
||||
} |
||||
BENCHMARK(BM_StdVectorFillString)->Range(0, 1024); |
||||
|
||||
struct Buffer { // some arbitrary structure for benchmarking.
|
||||
char* base; |
||||
int length; |
||||
int capacity; |
||||
void* user_data; |
||||
}; |
||||
|
||||
void BM_InlinedVectorTenAssignments(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) { |
||||
for (int i = 0; i < 10; ++i) { |
||||
dst = src; |
||||
} |
||||
} |
||||
} |
||||
BENCHMARK(BM_InlinedVectorTenAssignments) |
||||
->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> x(absl::InlinedVector<int, 4>{1, 2, 3}); |
||||
benchmark::DoNotOptimize(x); |
||||
} |
||||
} |
||||
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(LargeCopyableSwappable&& o) = delete; |
||||
|
||||
LargeCopyableSwappable& operator=(LargeCopyableSwappable o) { |
||||
using std::swap; |
||||
swap(*this, o); |
||||
return *this; |
||||
} |
||||
LargeCopyableSwappable& operator=(LargeCopyableSwappable&& o) = delete; |
||||
|
||||
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; |
||||
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) { |
||||
for (int i = 0; i < 1000; ++i) { |
||||
benchmark::DoNotOptimize(v); |
||||
benchmark::DoNotOptimize(v[4]); |
||||
} |
||||
} |
||||
state.SetItemsProcessed(1000 * static_cast<int64_t>(state.iterations())); |
||||
} |
||||
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) { |
||||
for (int i = 0; i < 1000; ++i) { |
||||
benchmark::DoNotOptimize(v); |
||||
benchmark::DoNotOptimize(v[4]); |
||||
} |
||||
} |
||||
state.SetItemsProcessed(1000 * static_cast<int64_t>(state.iterations())); |
||||
} |
||||
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) { |
||||
for (int i = 0; i < 1000; ++i) { |
||||
benchmark::DoNotOptimize(v); |
||||
benchmark::DoNotOptimize(v[4]); |
||||
} |
||||
} |
||||
state.SetItemsProcessed(1000 * static_cast<int64_t>(state.iterations())); |
||||
} |
||||
BENCHMARK(BM_StdVectorIndex); |
||||
|
||||
#define UNROLL_2(x) \ |
||||
benchmark::DoNotOptimize(x); \
|
||||
benchmark::DoNotOptimize(x); |
||||
|
||||
#define UNROLL_4(x) UNROLL_2(x) UNROLL_2(x) |
||||
#define UNROLL_8(x) UNROLL_4(x) UNROLL_4(x) |
||||
#define UNROLL_16(x) UNROLL_8(x) UNROLL_8(x); |
||||
|
||||
void BM_InlinedVectorDataInlined(benchmark::State& state) { |
||||
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7}; |
||||
for (auto _ : state) { |
||||
UNROLL_16(v.data()); |
||||
} |
||||
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations())); |
||||
} |
||||
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) { |
||||
UNROLL_16(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) { |
||||
UNROLL_16(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) { |
||||
UNROLL_16(v.size()); |
||||
} |
||||
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations())); |
||||
} |
||||
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) { |
||||
UNROLL_16(v.size()); |
||||
} |
||||
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations())); |
||||
} |
||||
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) { |
||||
UNROLL_16(v.size()); |
||||
} |
||||
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations())); |
||||
} |
||||
BENCHMARK(BM_StdVectorSize); |
||||
|
||||
void BM_InlinedVectorEmptyInlined(benchmark::State& state) { |
||||
absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7}; |
||||
for (auto _ : state) { |
||||
UNROLL_16(v.empty()); |
||||
} |
||||
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations())); |
||||
} |
||||
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) { |
||||
UNROLL_16(v.empty()); |
||||
} |
||||
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations())); |
||||
} |
||||
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) { |
||||
UNROLL_16(v.empty()); |
||||
} |
||||
state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations())); |
||||
} |
||||
BENCHMARK(BM_StdVectorEmpty); |
||||
|
||||
} // namespace
|
||||
|
||||
BENCHMARK_MAIN(); |
@ -0,0 +1,519 @@ |
||||
// Copyright 2017 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
|
||||
//
|
||||
// http://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 "absl/types/variant.h" |
||||
|
||||
#include <iostream> |
||||
#include <memory> |
||||
#include <utility> |
||||
#include <vector> |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
#include "absl/base/internal/exception_safety_testing.h" |
||||
#include "absl/memory/memory.h" |
||||
|
||||
namespace absl { |
||||
namespace { |
||||
|
||||
using ::testing::MakeExceptionSafetyTester; |
||||
using ::testing::nothrow_guarantee; |
||||
using ::testing::strong_guarantee; |
||||
using ::testing::TestThrowingCtor; |
||||
|
||||
using Thrower = testing::ThrowingValue<>; |
||||
using CopyNothrow = testing::ThrowingValue<testing::TypeSpec::kNoThrowCopy>; |
||||
using MoveNothrow = testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>; |
||||
using ThrowingAlloc = testing::ThrowingAllocator<Thrower>; |
||||
using ThrowerVec = std::vector<Thrower, ThrowingAlloc>; |
||||
using ThrowingVariant = |
||||
absl::variant<Thrower, CopyNothrow, MoveNothrow, ThrowerVec>; |
||||
|
||||
struct ConversionException {}; |
||||
|
||||
template <class T> |
||||
struct ExceptionOnConversion { |
||||
operator T() const { // NOLINT
|
||||
throw ConversionException(); |
||||
} |
||||
}; |
||||
|
||||
// Forces a variant into the valueless by exception state.
|
||||
void ToValuelessByException(ThrowingVariant& v) { // NOLINT
|
||||
try { |
||||
v.emplace<Thrower>(); |
||||
v.emplace<Thrower>(ExceptionOnConversion<Thrower>()); |
||||
} catch (ConversionException& /*e*/) { |
||||
// This space intentionally left blank.
|
||||
} |
||||
} |
||||
|
||||
// Check that variant is still in a usable state after an exception is thrown.
|
||||
testing::AssertionResult CheckInvariants(ThrowingVariant* v) { |
||||
using testing::AssertionFailure; |
||||
using testing::AssertionSuccess; |
||||
|
||||
// Try using the active alternative
|
||||
if (absl::holds_alternative<Thrower>(*v)) { |
||||
auto& t = absl::get<Thrower>(*v); |
||||
t = Thrower{-100}; |
||||
if (t.Get() != -100) { |
||||
return AssertionFailure() << "Thrower should be assigned -100"; |
||||
} |
||||
} else if (absl::holds_alternative<ThrowerVec>(*v)) { |
||||
auto& tv = absl::get<ThrowerVec>(*v); |
||||
tv.clear(); |
||||
tv.emplace_back(-100); |
||||
if (tv.size() != 1 || tv[0].Get() != -100) { |
||||
return AssertionFailure() << "ThrowerVec should be {Thrower{-100}}"; |
||||
} |
||||
} else if (absl::holds_alternative<CopyNothrow>(*v)) { |
||||
auto& t = absl::get<CopyNothrow>(*v); |
||||
t = CopyNothrow{-100}; |
||||
if (t.Get() != -100) { |
||||
return AssertionFailure() << "CopyNothrow should be assigned -100"; |
||||
} |
||||
} else if (absl::holds_alternative<MoveNothrow>(*v)) { |
||||
auto& t = absl::get<MoveNothrow>(*v); |
||||
t = MoveNothrow{-100}; |
||||
if (t.Get() != -100) { |
||||
return AssertionFailure() << "MoveNothrow should be assigned -100"; |
||||
} |
||||
} |
||||
|
||||
// Try making variant valueless_by_exception
|
||||
if (!v->valueless_by_exception()) ToValuelessByException(*v); |
||||
if (!v->valueless_by_exception()) { |
||||
return AssertionFailure() << "Variant should be valueless_by_exception"; |
||||
} |
||||
try { |
||||
auto unused = absl::get<Thrower>(*v); |
||||
static_cast<void>(unused); |
||||
return AssertionFailure() << "Variant should not contain Thrower"; |
||||
} catch (absl::bad_variant_access) { |
||||
} catch (...) { |
||||
return AssertionFailure() << "Unexpected exception throw from absl::get"; |
||||
} |
||||
|
||||
// Try using the variant
|
||||
v->emplace<Thrower>(100); |
||||
if (!absl::holds_alternative<Thrower>(*v) || |
||||
absl::get<Thrower>(*v) != Thrower(100)) { |
||||
return AssertionFailure() << "Variant should contain Thrower(100)"; |
||||
} |
||||
v->emplace<ThrowerVec>({Thrower(100)}); |
||||
if (!absl::holds_alternative<ThrowerVec>(*v) || |
||||
absl::get<ThrowerVec>(*v)[0] != Thrower(100)) { |
||||
return AssertionFailure() |
||||
<< "Variant should contain ThrowerVec{Thrower(100)}"; |
||||
} |
||||
return AssertionSuccess(); |
||||
} |
||||
|
||||
Thrower ExpectedThrower() { return Thrower(42); } |
||||
ThrowerVec ExpectedThrowerVec() { return {Thrower(100), Thrower(200)}; } |
||||
ThrowingVariant ValuelessByException() { |
||||
ThrowingVariant v; |
||||
ToValuelessByException(v); |
||||
return v; |
||||
} |
||||
ThrowingVariant WithThrower() { return Thrower(39); } |
||||
ThrowingVariant WithThrowerVec() { |
||||
return ThrowerVec{Thrower(1), Thrower(2), Thrower(3)}; |
||||
} |
||||
ThrowingVariant WithCopyNoThrow() { return CopyNothrow(39); } |
||||
ThrowingVariant WithMoveNoThrow() { return MoveNothrow(39); } |
||||
|
||||
TEST(VariantExceptionSafetyTest, DefaultConstructor) { |
||||
TestThrowingCtor<ThrowingVariant>(); |
||||
} |
||||
|
||||
TEST(VariantExceptionSafetyTest, CopyConstructor) { |
||||
{ |
||||
ThrowingVariant v(ExpectedThrower()); |
||||
TestThrowingCtor<ThrowingVariant>(v); |
||||
} |
||||
{ |
||||
ThrowingVariant v(ExpectedThrowerVec()); |
||||
TestThrowingCtor<ThrowingVariant>(v); |
||||
} |
||||
{ |
||||
ThrowingVariant v(ValuelessByException()); |
||||
TestThrowingCtor<ThrowingVariant>(v); |
||||
} |
||||
} |
||||
|
||||
TEST(VariantExceptionSafetyTest, MoveConstructor) { |
||||
{ |
||||
ThrowingVariant v(ExpectedThrower()); |
||||
TestThrowingCtor<ThrowingVariant>(std::move(v)); |
||||
} |
||||
{ |
||||
ThrowingVariant v(ExpectedThrowerVec()); |
||||
TestThrowingCtor<ThrowingVariant>(std::move(v)); |
||||
} |
||||
{ |
||||
ThrowingVariant v(ValuelessByException()); |
||||
TestThrowingCtor<ThrowingVariant>(std::move(v)); |
||||
} |
||||
} |
||||
|
||||
TEST(VariantExceptionSafetyTest, ValueConstructor) { |
||||
TestThrowingCtor<ThrowingVariant>(ExpectedThrower()); |
||||
TestThrowingCtor<ThrowingVariant>(ExpectedThrowerVec()); |
||||
} |
||||
|
||||
TEST(VariantExceptionSafetyTest, InPlaceTypeConstructor) { |
||||
TestThrowingCtor<ThrowingVariant>(absl::in_place_type_t<Thrower>{}, |
||||
ExpectedThrower()); |
||||
TestThrowingCtor<ThrowingVariant>(absl::in_place_type_t<ThrowerVec>{}, |
||||
ExpectedThrowerVec()); |
||||
} |
||||
|
||||
TEST(VariantExceptionSafetyTest, InPlaceIndexConstructor) { |
||||
TestThrowingCtor<ThrowingVariant>(absl::in_place_index_t<0>{}, |
||||
ExpectedThrower()); |
||||
TestThrowingCtor<ThrowingVariant>(absl::in_place_index_t<3>{}, |
||||
ExpectedThrowerVec()); |
||||
} |
||||
|
||||
TEST(VariantExceptionSafetyTest, CopyAssign) { |
||||
// variant& operator=(const variant& rhs);
|
||||
// Let j be rhs.index()
|
||||
{ |
||||
// - neither *this nor rhs holds a value
|
||||
const ThrowingVariant rhs = ValuelessByException(); |
||||
EXPECT_TRUE(MakeExceptionSafetyTester() |
||||
.WithInitialValue(ValuelessByException()) |
||||
.WithInvariants(nothrow_guarantee) |
||||
.Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; })); |
||||
} |
||||
{ |
||||
// - *this holds a value but rhs does not
|
||||
const ThrowingVariant rhs = ValuelessByException(); |
||||
EXPECT_TRUE(MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithThrower()) |
||||
.WithInvariants(nothrow_guarantee) |
||||
.Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; })); |
||||
} |
||||
// - index() == j
|
||||
{ |
||||
const ThrowingVariant rhs(ExpectedThrower()); |
||||
auto tester = |
||||
MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithThrower()) |
||||
.WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }); |
||||
EXPECT_TRUE(tester.WithInvariants(CheckInvariants).Test()); |
||||
EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test()); |
||||
} |
||||
{ |
||||
const ThrowingVariant rhs(ExpectedThrowerVec()); |
||||
auto tester = |
||||
MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithThrowerVec()) |
||||
.WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }); |
||||
EXPECT_TRUE(tester.WithInvariants(CheckInvariants).Test()); |
||||
EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test()); |
||||
} |
||||
// libstdc++ std::variant has bugs on copy assignment regarding exception
|
||||
// safety.
|
||||
#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) |
||||
// index() != j
|
||||
// if is_nothrow_copy_constructible_v<Tj> or
|
||||
// !is_nothrow_move_constructible<Tj> is true, equivalent to
|
||||
// emplace<j>(get<j>(rhs))
|
||||
{ |
||||
// is_nothrow_copy_constructible_v<Tj> == true
|
||||
// should not throw because emplace() invokes Tj's copy ctor
|
||||
// which should not throw.
|
||||
const ThrowingVariant rhs(CopyNothrow{}); |
||||
EXPECT_TRUE(MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithThrower()) |
||||
.WithInvariants(nothrow_guarantee) |
||||
.Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; })); |
||||
} |
||||
{ |
||||
// is_nothrow_copy_constructible<Tj> == false &&
|
||||
// is_nothrow_move_constructible<Tj> == false
|
||||
// should provide basic guarantee because emplace() invokes Tj's copy ctor
|
||||
// which may throw.
|
||||
const ThrowingVariant rhs(ExpectedThrower()); |
||||
auto tester = |
||||
MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithCopyNoThrow()) |
||||
.WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }); |
||||
EXPECT_TRUE(tester |
||||
.WithInvariants(CheckInvariants, |
||||
[](ThrowingVariant* lhs) { |
||||
return lhs->valueless_by_exception(); |
||||
}) |
||||
.Test()); |
||||
EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test()); |
||||
} |
||||
#endif // !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
|
||||
{ |
||||
// is_nothrow_copy_constructible_v<Tj> == false &&
|
||||
// is_nothrow_move_constructible_v<Tj> == true
|
||||
// should provide strong guarantee because it is equivalent to
|
||||
// operator=(variant(rhs)) which creates a temporary then invoke the move
|
||||
// ctor which shouldn't throw.
|
||||
const ThrowingVariant rhs(MoveNothrow{}); |
||||
EXPECT_TRUE(MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithThrower()) |
||||
.WithInvariants(CheckInvariants, strong_guarantee) |
||||
.Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; })); |
||||
} |
||||
} |
||||
|
||||
TEST(VariantExceptionSafetyTest, MoveAssign) { |
||||
// variant& operator=(variant&& rhs);
|
||||
// Let j be rhs.index()
|
||||
{ |
||||
// - neither *this nor rhs holds a value
|
||||
ThrowingVariant rhs = ValuelessByException(); |
||||
|
||||
EXPECT_TRUE(MakeExceptionSafetyTester() |
||||
.WithInitialValue(ValuelessByException()) |
||||
.WithInvariants(nothrow_guarantee) |
||||
.Test([rhs](ThrowingVariant* lhs) mutable { |
||||
*lhs = std::move(rhs); |
||||
})); |
||||
} |
||||
{ |
||||
// - *this holds a value but rhs does not
|
||||
ThrowingVariant rhs = ValuelessByException(); |
||||
EXPECT_TRUE(MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithThrower()) |
||||
.WithInvariants(nothrow_guarantee) |
||||
.Test([rhs](ThrowingVariant* lhs) mutable { |
||||
*lhs = std::move(rhs); |
||||
})); |
||||
} |
||||
{ |
||||
// - index() == j
|
||||
// assign get<j>(std::move(rhs)) to the value contained in *this.
|
||||
// If an exception is thrown during call to Tj's move assignment, the state
|
||||
// of the contained value is as defined by the exception safety guarantee of
|
||||
// Tj's move assignment; index() will be j.
|
||||
ThrowingVariant rhs(ExpectedThrower()); |
||||
size_t j = rhs.index(); |
||||
// Since Thrower's move assignment has basic guarantee, so should variant's.
|
||||
auto tester = MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithThrower()) |
||||
.WithOperation([rhs](ThrowingVariant* lhs) mutable { |
||||
*lhs = std::move(rhs); |
||||
}); |
||||
EXPECT_TRUE(tester |
||||
.WithInvariants( |
||||
CheckInvariants, |
||||
[j](ThrowingVariant* lhs) { return lhs->index() == j; }) |
||||
.Test()); |
||||
EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test()); |
||||
} |
||||
{ |
||||
// - otherwise (index() != j), equivalent to
|
||||
// emplace<j>(get<j>(std::move(rhs)))
|
||||
// - If an exception is thrown during the call to Tj's move construction
|
||||
// (with j being rhs.index()), the variant will hold no value.
|
||||
ThrowingVariant rhs(CopyNothrow{}); |
||||
EXPECT_TRUE(MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithThrower()) |
||||
.WithInvariants(CheckInvariants, |
||||
[](ThrowingVariant* lhs) { |
||||
return lhs->valueless_by_exception(); |
||||
}) |
||||
.Test([rhs](ThrowingVariant* lhs) mutable { |
||||
*lhs = std::move(rhs); |
||||
})); |
||||
} |
||||
} |
||||
|
||||
TEST(VariantExceptionSafetyTest, ValueAssign) { |
||||
// template<class T> variant& operator=(T&& t);
|
||||
// Let Tj be the type that is selected by overload resolution to be assigned.
|
||||
{ |
||||
// If *this holds a Tj, assigns std::forward<T>(t) to the value contained in
|
||||
// *this. If an exception is thrown during the assignment of
|
||||
// std::forward<T>(t) to the value contained in *this, the state of the
|
||||
// contained value and t are as defined by the exception safety guarantee of
|
||||
// the assignment expression; valueless_by_exception() will be false.
|
||||
// Since Thrower's copy/move assignment has basic guarantee, so should
|
||||
// variant's.
|
||||
Thrower rhs = ExpectedThrower(); |
||||
// copy assign
|
||||
auto copy_tester = |
||||
MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithThrower()) |
||||
.WithOperation([rhs](ThrowingVariant* lhs) { *lhs = rhs; }); |
||||
EXPECT_TRUE(copy_tester |
||||
.WithInvariants(CheckInvariants, |
||||
[](ThrowingVariant* lhs) { |
||||
return !lhs->valueless_by_exception(); |
||||
}) |
||||
.Test()); |
||||
EXPECT_FALSE(copy_tester.WithInvariants(strong_guarantee).Test()); |
||||
// move assign
|
||||
auto move_tester = MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithThrower()) |
||||
.WithOperation([rhs](ThrowingVariant* lhs) mutable { |
||||
*lhs = std::move(rhs); |
||||
}); |
||||
EXPECT_TRUE(move_tester |
||||
.WithInvariants(CheckInvariants, |
||||
[](ThrowingVariant* lhs) { |
||||
return !lhs->valueless_by_exception(); |
||||
}) |
||||
.Test()); |
||||
|
||||
EXPECT_FALSE(move_tester.WithInvariants(strong_guarantee).Test()); |
||||
} |
||||
// Otherwise (*this holds something else), if is_nothrow_constructible_v<Tj,
|
||||
// T> || !is_nothrow_move_constructible_v<Tj> is true, equivalent to
|
||||
// emplace<j>(std::forward<T>(t)).
|
||||
// We simplify the test by letting T = `const Tj&` or `Tj&&`, so we can reuse
|
||||
// the CopyNothrow and MoveNothrow types.
|
||||
|
||||
// if is_nothrow_constructible_v<Tj, T>
|
||||
// (i.e. is_nothrow_copy/move_constructible_v<Tj>) is true, emplace() just
|
||||
// invokes the copy/move constructor and it should not throw.
|
||||
{ |
||||
const CopyNothrow rhs; |
||||
EXPECT_TRUE(MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithThrower()) |
||||
.WithInvariants(nothrow_guarantee) |
||||
.Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; })); |
||||
} |
||||
{ |
||||
MoveNothrow rhs; |
||||
EXPECT_TRUE(MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithThrower()) |
||||
.WithInvariants(nothrow_guarantee) |
||||
.Test([rhs](ThrowingVariant* lhs) mutable { |
||||
*lhs = std::move(rhs); |
||||
})); |
||||
} |
||||
// if is_nothrow_constructible_v<Tj, T> == false &&
|
||||
// is_nothrow_move_constructible<Tj> == false
|
||||
// emplace() invokes the copy/move constructor which may throw so it should
|
||||
// provide basic guarantee and variant object might not hold a value.
|
||||
{ |
||||
Thrower rhs = ExpectedThrower(); |
||||
// copy
|
||||
auto copy_tester = |
||||
MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithCopyNoThrow()) |
||||
.WithOperation([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }); |
||||
EXPECT_TRUE(copy_tester |
||||
.WithInvariants(CheckInvariants, |
||||
[](ThrowingVariant* lhs) { |
||||
return lhs->valueless_by_exception(); |
||||
}) |
||||
.Test()); |
||||
EXPECT_FALSE(copy_tester.WithInvariants(strong_guarantee).Test()); |
||||
// move
|
||||
auto move_tester = MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithCopyNoThrow()) |
||||
.WithOperation([rhs](ThrowingVariant* lhs) mutable { |
||||
*lhs = std::move(rhs); |
||||
}); |
||||
EXPECT_TRUE(move_tester |
||||
.WithInvariants(CheckInvariants, |
||||
[](ThrowingVariant* lhs) { |
||||
return lhs->valueless_by_exception(); |
||||
}) |
||||
.Test()); |
||||
EXPECT_FALSE(move_tester.WithInvariants(strong_guarantee).Test()); |
||||
} |
||||
// Otherwise (if is_nothrow_constructible_v<Tj, T> == false &&
|
||||
// is_nothrow_move_constructible<Tj> == true),
|
||||
// equivalent to operator=(variant(std::forward<T>(t)))
|
||||
// This should have strong guarantee because it creates a temporary variant
|
||||
// and operator=(variant&&) invokes Tj's move ctor which doesn't throw.
|
||||
// libstdc++ std::variant has bugs on conversion assignment regarding
|
||||
// exception safety.
|
||||
#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) |
||||
{ |
||||
MoveNothrow rhs; |
||||
EXPECT_TRUE(MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithThrower()) |
||||
.WithInvariants(CheckInvariants, strong_guarantee) |
||||
.Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; })); |
||||
} |
||||
#endif // !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
|
||||
} |
||||
|
||||
TEST(VariantExceptionSafetyTest, Emplace) { |
||||
// If an exception during the initialization of the contained value, the
|
||||
// variant might not hold a value. The standard requires emplace() to provide
|
||||
// only basic guarantee.
|
||||
{ |
||||
Thrower args = ExpectedThrower(); |
||||
auto tester = MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithThrower()) |
||||
.WithOperation([&args](ThrowingVariant* v) { |
||||
v->emplace<Thrower>(args); |
||||
}); |
||||
EXPECT_TRUE(tester |
||||
.WithInvariants(CheckInvariants, |
||||
[](ThrowingVariant* v) { |
||||
return v->valueless_by_exception(); |
||||
}) |
||||
.Test()); |
||||
EXPECT_FALSE(tester.WithInvariants(strong_guarantee).Test()); |
||||
} |
||||
} |
||||
|
||||
TEST(VariantExceptionSafetyTest, Swap) { |
||||
// if both are valueless_by_exception(), no effect
|
||||
{ |
||||
ThrowingVariant rhs = ValuelessByException(); |
||||
EXPECT_TRUE( |
||||
MakeExceptionSafetyTester() |
||||
.WithInitialValue(ValuelessByException()) |
||||
.WithInvariants(nothrow_guarantee) |
||||
.Test([rhs](ThrowingVariant* lhs) mutable { lhs->swap(rhs); })); |
||||
} |
||||
// if index() == rhs.index(), calls swap(get<i>(*this), get<i>(rhs))
|
||||
// where i is index().
|
||||
{ |
||||
ThrowingVariant rhs = ExpectedThrower(); |
||||
EXPECT_TRUE( |
||||
MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithThrower()) |
||||
.WithInvariants(CheckInvariants) |
||||
.Test([rhs](ThrowingVariant* lhs) mutable { lhs->swap(rhs); })); |
||||
} |
||||
// Otherwise, exchanges the value of rhs and *this. The exception safety
|
||||
// involves variant in moved-from state which is not specified in the
|
||||
// standard, and since swap is 3-step it's impossible for it to provide a
|
||||
// overall strong guarantee. So, we are only checking basic guarantee here.
|
||||
{ |
||||
ThrowingVariant rhs = ExpectedThrower(); |
||||
EXPECT_TRUE( |
||||
MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithCopyNoThrow()) |
||||
.WithInvariants(CheckInvariants) |
||||
.Test([rhs](ThrowingVariant* lhs) mutable { lhs->swap(rhs); })); |
||||
} |
||||
{ |
||||
ThrowingVariant rhs = ExpectedThrower(); |
||||
EXPECT_TRUE( |
||||
MakeExceptionSafetyTester() |
||||
.WithInitialValue(WithCopyNoThrow()) |
||||
.WithInvariants(CheckInvariants) |
||||
.Test([rhs](ThrowingVariant* lhs) mutable { rhs.swap(*lhs); })); |
||||
} |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace absl
|
Loading…
Reference in new issue