- b9a479321581cd0293f124041bf5c06f456afec1 Adds exception safety tests for absl::make_unique<T>(...) by Abseil Team <absl-team@google.com>
- 78c61364007f6ab66155c151d0061bbec89c3dbd Update variadic visitation to use a switch statement when... by Matt Calabrese <calabrese@google.com> - b62eb9546087e0001307a741fcdf023b2d156966 Merge GitHub PR #130 - Add MIPS support to GetProgramCoun... by Derek Mauro <dmauro@google.com> - 09ab5739de33c8f1bebab2bb70bf7d4331348f05 Update ABSL_ASSERT to silence clang-tidy warnings about c... by Matt Calabrese <calabrese@google.com> - e73ee389ce8fe1a90738973c219ebbb19bb389f3 Update unary visitation to use a switch statement when th... by Matt Calabrese <calabrese@google.com> - c8734ccf475b856c95220f21a5ec4f44302cb5ce Work around a MSVC bug for absl::variant, by making `Acce... by Xiaoyi Zhang <zhangxy@google.com> GitOrigin-RevId: b9a479321581cd0293f124041bf5c06f456afec1 Change-Id: Idb6fc906087c0a4e6fc5c75a391c7f73101c613e
This commit is contained in:
parent
e5be80532b
commit
eb686c069f
8 changed files with 610 additions and 40 deletions
|
@ -195,7 +195,8 @@ enum LinkerInitialized {
|
||||||
#define ABSL_ASSERT(expr) (false ? (void)(expr) : (void)0)
|
#define ABSL_ASSERT(expr) (false ? (void)(expr) : (void)0)
|
||||||
#else
|
#else
|
||||||
#define ABSL_ASSERT(expr) \
|
#define ABSL_ASSERT(expr) \
|
||||||
(ABSL_PREDICT_TRUE((expr)) ? (void)0 : [] { assert(false && #expr); }())
|
(ABSL_PREDICT_TRUE((expr)) ? (void)0 \
|
||||||
|
: [] { assert(false && #expr); }()) // NOLINT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // ABSL_BASE_MACROS_H_
|
#endif // ABSL_BASE_MACROS_H_
|
||||||
|
|
|
@ -18,6 +18,7 @@ load(
|
||||||
"//absl:copts.bzl",
|
"//absl:copts.bzl",
|
||||||
"ABSL_DEFAULT_COPTS",
|
"ABSL_DEFAULT_COPTS",
|
||||||
"ABSL_TEST_COPTS",
|
"ABSL_TEST_COPTS",
|
||||||
|
"ABSL_EXCEPTIONS_FLAG",
|
||||||
)
|
)
|
||||||
|
|
||||||
package(default_visibility = ["//visibility:public"])
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
@ -45,3 +46,16 @@ cc_test(
|
||||||
"@com_google_googletest//:gtest_main",
|
"@com_google_googletest//:gtest_main",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "memory_exception_safety_test",
|
||||||
|
srcs = [
|
||||||
|
"memory_exception_safety_test.cc",
|
||||||
|
],
|
||||||
|
copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
|
||||||
|
deps = [
|
||||||
|
":memory",
|
||||||
|
"//absl/base:exception_safety_testing",
|
||||||
|
"@com_google_googletest//:gtest_main",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
|
@ -49,4 +49,23 @@ absl_test(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# test memory_exception_safety_test
|
||||||
|
set(MEMORY_EXCEPTION_SAFETY_TEST_SRC "memory_exception_safety_test.cc")
|
||||||
|
set(MEMORY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES
|
||||||
|
absl::memory
|
||||||
|
absl_base_internal_exception_safety_testing
|
||||||
|
)
|
||||||
|
|
||||||
|
absl_test(
|
||||||
|
TARGET
|
||||||
|
memory_exception_safety_test
|
||||||
|
SOURCES
|
||||||
|
${MEMORY_EXCEPTION_SAFETY_TEST_SRC}
|
||||||
|
PUBLIC_LIBRARIES
|
||||||
|
${MEMORY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES}
|
||||||
|
PRIVATE_COMPILE_FLAGS
|
||||||
|
${ABSL_EXCEPTIONS_FLAG}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
49
absl/memory/memory_exception_safety_test.cc
Normal file
49
absl/memory/memory_exception_safety_test.cc
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// 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/memory/memory.h"
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "absl/base/internal/exception_safety_testing.h"
|
||||||
|
|
||||||
|
namespace absl {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using Thrower = ::testing::ThrowingValue<>;
|
||||||
|
|
||||||
|
TEST(MakeUnique, CheckForLeaks) {
|
||||||
|
constexpr int kValue = 321;
|
||||||
|
constexpr size_t kLength = 10;
|
||||||
|
auto tester = testing::MakeExceptionSafetyTester()
|
||||||
|
.WithInitialValue(Thrower(kValue))
|
||||||
|
// Ensures make_unique does not modify the input. The real
|
||||||
|
// test, though, is ConstructorTracker checking for leaks.
|
||||||
|
.WithInvariants(testing::strong_guarantee);
|
||||||
|
|
||||||
|
EXPECT_TRUE(tester.Test([](Thrower* thrower) {
|
||||||
|
static_cast<void>(absl::make_unique<Thrower>(*thrower));
|
||||||
|
}));
|
||||||
|
|
||||||
|
EXPECT_TRUE(tester.Test([](Thrower* thrower) {
|
||||||
|
static_cast<void>(absl::make_unique<Thrower>(std::move(*thrower)));
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Test T[n] overload
|
||||||
|
EXPECT_TRUE(tester.Test([&](Thrower*) {
|
||||||
|
static_cast<void>(absl::make_unique<Thrower[]>(kLength));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace absl
|
|
@ -247,6 +247,19 @@ cc_test(
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "variant_benchmark",
|
||||||
|
srcs = [
|
||||||
|
"variant_benchmark.cc",
|
||||||
|
],
|
||||||
|
copts = ABSL_TEST_COPTS,
|
||||||
|
deps = [
|
||||||
|
":variant",
|
||||||
|
"//absl/utility",
|
||||||
|
"@com_github_google_benchmark//:benchmark_main",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
cc_test(
|
cc_test(
|
||||||
name = "variant_exception_safety_test",
|
name = "variant_exception_safety_test",
|
||||||
size = "small",
|
size = "small",
|
||||||
|
|
|
@ -19,15 +19,19 @@
|
||||||
#ifndef ABSL_TYPES_variant_internal_H_
|
#ifndef ABSL_TYPES_variant_internal_H_
|
||||||
#define ABSL_TYPES_variant_internal_H_
|
#define ABSL_TYPES_variant_internal_H_
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <cstdlib>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "absl/base/config.h"
|
||||||
#include "absl/base/internal/identity.h"
|
#include "absl/base/internal/identity.h"
|
||||||
#include "absl/base/internal/inline_variable.h"
|
#include "absl/base/internal/inline_variable.h"
|
||||||
#include "absl/base/internal/invoke.h"
|
#include "absl/base/internal/invoke.h"
|
||||||
|
#include "absl/base/macros.h"
|
||||||
#include "absl/base/optimization.h"
|
#include "absl/base/optimization.h"
|
||||||
#include "absl/meta/type_traits.h"
|
#include "absl/meta/type_traits.h"
|
||||||
#include "absl/types/bad_variant_access.h"
|
#include "absl/types/bad_variant_access.h"
|
||||||
|
@ -119,6 +123,8 @@ using GiveQualsToT = typename GiveQualsTo<T, U>::type;
|
||||||
template <std::size_t I>
|
template <std::size_t I>
|
||||||
using SizeT = std::integral_constant<std::size_t, I>;
|
using SizeT = std::integral_constant<std::size_t, I>;
|
||||||
|
|
||||||
|
using NPos = SizeT<variant_npos>;
|
||||||
|
|
||||||
template <class Variant, class T, class = void>
|
template <class Variant, class T, class = void>
|
||||||
struct IndexOfConstructedType {};
|
struct IndexOfConstructedType {};
|
||||||
|
|
||||||
|
@ -248,19 +254,270 @@ struct MakeVisitationMatrix<ReturnType, FunctionObject,
|
||||||
ReturnType, FunctionObject, index_sequence<TailEndIndices...>,
|
ReturnType, FunctionObject, index_sequence<TailEndIndices...>,
|
||||||
absl::make_index_sequence<HeadEndIndex>, BoundIndices...> {};
|
absl::make_index_sequence<HeadEndIndex>, BoundIndices...> {};
|
||||||
|
|
||||||
template <std::size_t... EndIndices, class Op, class... SizeT>
|
struct UnreachableSwitchCase {
|
||||||
VisitIndicesResultT<Op, SizeT...> visit_indices(Op&& op, SizeT... indices) {
|
template <class Op>
|
||||||
return AccessSimpleArray(
|
[[noreturn]] static VisitIndicesResultT<Op, std::size_t> Run(
|
||||||
MakeVisitationMatrix<VisitIndicesResultT<Op, SizeT...>, Op,
|
Op&& /*ignored*/) {
|
||||||
index_sequence<(EndIndices + 1)...>>::Run(),
|
#if ABSL_HAVE_BUILTIN(__builtin_unreachable) || \
|
||||||
(indices + 1)...)(absl::forward<Op>(op));
|
(defined(__GNUC__) && !defined(__clang__))
|
||||||
|
__builtin_unreachable();
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
__assume(false);
|
||||||
|
#else
|
||||||
|
// Try to use assert of false being identified as an unreachable intrinsic.
|
||||||
|
// NOTE: We use assert directly to increase chances of exploiting an assume
|
||||||
|
// intrinsic.
|
||||||
|
assert(false); // NOLINT
|
||||||
|
|
||||||
|
// Hack to silence potential no return warning -- cause an infinite loop.
|
||||||
|
return Run(absl::forward<Op>(op));
|
||||||
|
#endif // Checks for __builtin_unreachable
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Op, std::size_t I>
|
||||||
|
struct ReachableSwitchCase {
|
||||||
|
static VisitIndicesResultT<Op, std::size_t> Run(Op&& op) {
|
||||||
|
return absl::base_internal::Invoke(absl::forward<Op>(op), SizeT<I>());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The number 33 is just a guess at a reasonable maximum to our switch. It is
|
||||||
|
// not based on any analysis. The reason it is a power of 2 plus 1 instead of a
|
||||||
|
// power of 2 is because the number was picked to correspond to a power of 2
|
||||||
|
// amount of "normal" alternatives, plus one for the possibility of the user
|
||||||
|
// providing "monostate" in addition to the more natural alternatives.
|
||||||
|
ABSL_INTERNAL_INLINE_CONSTEXPR(std::size_t, MaxUnrolledVisitCases, 33);
|
||||||
|
|
||||||
|
// Note: The default-definition is for unreachable cases.
|
||||||
|
template <bool IsReachable>
|
||||||
|
struct PickCaseImpl {
|
||||||
|
template <class Op, std::size_t I>
|
||||||
|
using Apply = UnreachableSwitchCase;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct PickCaseImpl</*IsReachable =*/true> {
|
||||||
|
template <class Op, std::size_t I>
|
||||||
|
using Apply = ReachableSwitchCase<Op, I>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Note: This form of dance with template aliases is to make sure that we
|
||||||
|
// instantiate a number of templates proportional to the number of variant
|
||||||
|
// alternatives rather than a number of templates proportional to our
|
||||||
|
// maximum unrolled amount of visitation cases (aliases are effectively
|
||||||
|
// "free" whereas other template instantiations are costly).
|
||||||
|
template <class Op, std::size_t I, std::size_t EndIndex>
|
||||||
|
using PickCase = typename PickCaseImpl<(I < EndIndex)>::template Apply<Op, I>;
|
||||||
|
|
||||||
template <class ReturnType>
|
template <class ReturnType>
|
||||||
[[noreturn]] ReturnType TypedThrowBadVariantAccess() {
|
[[noreturn]] ReturnType TypedThrowBadVariantAccess() {
|
||||||
absl::variant_internal::ThrowBadVariantAccess();
|
absl::variant_internal::ThrowBadVariantAccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Given N variant sizes, determine the number of cases there would need to be
|
||||||
|
// in a single switch-statement that would cover every possibility in the
|
||||||
|
// corresponding N-ary visit operation.
|
||||||
|
template <std::size_t... NumAlternatives>
|
||||||
|
struct NumCasesOfSwitch;
|
||||||
|
|
||||||
|
template <std::size_t HeadNumAlternatives, std::size_t... TailNumAlternatives>
|
||||||
|
struct NumCasesOfSwitch<HeadNumAlternatives, TailNumAlternatives...> {
|
||||||
|
static constexpr std::size_t value =
|
||||||
|
(HeadNumAlternatives + 1) *
|
||||||
|
NumCasesOfSwitch<TailNumAlternatives...>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct NumCasesOfSwitch<> {
|
||||||
|
static constexpr std::size_t value = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A switch statement optimizes better than the table of function pointers.
|
||||||
|
template <std::size_t EndIndex>
|
||||||
|
struct VisitIndicesSwitch {
|
||||||
|
static_assert(EndIndex <= MaxUnrolledVisitCases,
|
||||||
|
"Maximum unrolled switch size exceeded.");
|
||||||
|
|
||||||
|
template <class Op>
|
||||||
|
static VisitIndicesResultT<Op, std::size_t> Run(Op&& op, std::size_t i) {
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
return PickCase<Op, 0, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 1:
|
||||||
|
return PickCase<Op, 1, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 2:
|
||||||
|
return PickCase<Op, 2, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 3:
|
||||||
|
return PickCase<Op, 3, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 4:
|
||||||
|
return PickCase<Op, 4, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 5:
|
||||||
|
return PickCase<Op, 5, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 6:
|
||||||
|
return PickCase<Op, 6, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 7:
|
||||||
|
return PickCase<Op, 7, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 8:
|
||||||
|
return PickCase<Op, 8, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 9:
|
||||||
|
return PickCase<Op, 9, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 10:
|
||||||
|
return PickCase<Op, 10, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 11:
|
||||||
|
return PickCase<Op, 11, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 12:
|
||||||
|
return PickCase<Op, 12, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 13:
|
||||||
|
return PickCase<Op, 13, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 14:
|
||||||
|
return PickCase<Op, 14, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 15:
|
||||||
|
return PickCase<Op, 15, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 16:
|
||||||
|
return PickCase<Op, 16, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 17:
|
||||||
|
return PickCase<Op, 17, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 18:
|
||||||
|
return PickCase<Op, 18, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 19:
|
||||||
|
return PickCase<Op, 19, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 20:
|
||||||
|
return PickCase<Op, 20, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 21:
|
||||||
|
return PickCase<Op, 21, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 22:
|
||||||
|
return PickCase<Op, 22, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 23:
|
||||||
|
return PickCase<Op, 23, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 24:
|
||||||
|
return PickCase<Op, 24, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 25:
|
||||||
|
return PickCase<Op, 25, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 26:
|
||||||
|
return PickCase<Op, 26, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 27:
|
||||||
|
return PickCase<Op, 27, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 28:
|
||||||
|
return PickCase<Op, 28, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 29:
|
||||||
|
return PickCase<Op, 29, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 30:
|
||||||
|
return PickCase<Op, 30, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 31:
|
||||||
|
return PickCase<Op, 31, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
case 32:
|
||||||
|
return PickCase<Op, 32, EndIndex>::Run(absl::forward<Op>(op));
|
||||||
|
default:
|
||||||
|
ABSL_ASSERT(i == variant_npos);
|
||||||
|
return absl::base_internal::Invoke(absl::forward<Op>(op), NPos());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t... EndIndices>
|
||||||
|
struct VisitIndicesFallback {
|
||||||
|
template <class Op, class... SizeT>
|
||||||
|
static VisitIndicesResultT<Op, SizeT...> Run(Op&& op, SizeT... indices) {
|
||||||
|
return AccessSimpleArray(
|
||||||
|
MakeVisitationMatrix<VisitIndicesResultT<Op, SizeT...>, Op,
|
||||||
|
index_sequence<(EndIndices + 1)...>>::Run(),
|
||||||
|
(indices + 1)...)(absl::forward<Op>(op));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Take an N-dimensional series of indices and convert them into a single index
|
||||||
|
// without loss of information. The purpose of this is to be able to convert an
|
||||||
|
// N-ary visit operation into a single switch statement.
|
||||||
|
template <std::size_t...>
|
||||||
|
struct FlattenIndices;
|
||||||
|
|
||||||
|
template <std::size_t HeadSize, std::size_t... TailSize>
|
||||||
|
struct FlattenIndices<HeadSize, TailSize...> {
|
||||||
|
template<class... SizeType>
|
||||||
|
static constexpr std::size_t Run(std::size_t head, SizeType... tail) {
|
||||||
|
return head + HeadSize * FlattenIndices<TailSize...>::Run(tail...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct FlattenIndices<> {
|
||||||
|
static constexpr std::size_t Run() { return 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Take a single "flattened" index (flattened by FlattenIndices) and determine
|
||||||
|
// the value of the index of one of the logically represented dimensions.
|
||||||
|
template <std::size_t I, std::size_t IndexToGet, std::size_t HeadSize,
|
||||||
|
std::size_t... TailSize>
|
||||||
|
struct UnflattenIndex {
|
||||||
|
static constexpr std::size_t value =
|
||||||
|
UnflattenIndex<I / HeadSize, IndexToGet - 1, TailSize...>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t I, std::size_t HeadSize, std::size_t... TailSize>
|
||||||
|
struct UnflattenIndex<I, 0, HeadSize, TailSize...> {
|
||||||
|
static constexpr std::size_t value = (I % HeadSize);
|
||||||
|
};
|
||||||
|
|
||||||
|
// The backend for converting an N-ary visit operation into a unary visit.
|
||||||
|
template <class IndexSequence, std::size_t... EndIndices>
|
||||||
|
struct VisitIndicesVariadicImpl;
|
||||||
|
|
||||||
|
template <std::size_t... N, std::size_t... EndIndices>
|
||||||
|
struct VisitIndicesVariadicImpl<absl::index_sequence<N...>, EndIndices...> {
|
||||||
|
// A type that can take an N-ary function object and converts it to a unary
|
||||||
|
// function object that takes a single, flattened index, and "unflattens" it
|
||||||
|
// into its individual dimensions when forwarding to the wrapped object.
|
||||||
|
template <class Op>
|
||||||
|
struct FlattenedOp {
|
||||||
|
template <std::size_t I>
|
||||||
|
VisitIndicesResultT<Op, decltype(EndIndices)...> operator()(
|
||||||
|
SizeT<I> /*index*/) && {
|
||||||
|
return base_internal::Invoke(
|
||||||
|
absl::forward<Op>(op),
|
||||||
|
SizeT<UnflattenIndex<I, N, (EndIndices + 1)...>::value -
|
||||||
|
std::size_t{1}>()...);
|
||||||
|
}
|
||||||
|
|
||||||
|
Op&& op;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Op, class... SizeType>
|
||||||
|
static VisitIndicesResultT<Op, decltype(EndIndices)...> Run(
|
||||||
|
Op&& op, SizeType... i) {
|
||||||
|
return VisitIndicesSwitch<NumCasesOfSwitch<EndIndices...>::value>::Run(
|
||||||
|
FlattenedOp<Op>{absl::forward<Op>(op)},
|
||||||
|
FlattenIndices<(EndIndices + std::size_t{1})...>::Run(
|
||||||
|
(i + std::size_t{1})...));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t... EndIndices>
|
||||||
|
struct VisitIndicesVariadic
|
||||||
|
: VisitIndicesVariadicImpl<absl::make_index_sequence<sizeof...(EndIndices)>,
|
||||||
|
EndIndices...> {};
|
||||||
|
|
||||||
|
// This implementation will flatten N-ary visit operations into a single switch
|
||||||
|
// statement when the number of cases would be less than our maximum specified
|
||||||
|
// switch-statement size.
|
||||||
|
// TODO(calabrese)
|
||||||
|
// Based on benchmarks, determine whether the function table approach actually
|
||||||
|
// does optimize better than a chain of switch statements and possibly update
|
||||||
|
// the implementation accordingly. Also consider increasing the maximum switch
|
||||||
|
// size.
|
||||||
|
template <std::size_t... EndIndices>
|
||||||
|
struct VisitIndices
|
||||||
|
: absl::conditional_t<(NumCasesOfSwitch<EndIndices...>::value <=
|
||||||
|
MaxUnrolledVisitCases),
|
||||||
|
VisitIndicesVariadic<EndIndices...>,
|
||||||
|
VisitIndicesFallback<EndIndices...>> {};
|
||||||
|
|
||||||
|
template <std::size_t EndIndex>
|
||||||
|
struct VisitIndices<EndIndex>
|
||||||
|
: absl::conditional_t<(EndIndex <= MaxUnrolledVisitCases),
|
||||||
|
VisitIndicesSwitch<EndIndex>,
|
||||||
|
VisitIndicesFallback<EndIndex>> {};
|
||||||
|
|
||||||
// Suppress bogus warning on MSVC: MSVC complains that the `reinterpret_cast`
|
// Suppress bogus warning on MSVC: MSVC complains that the `reinterpret_cast`
|
||||||
// below is returning the address of a temporary or local object.
|
// below is returning the address of a temporary or local object.
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
@ -270,8 +527,10 @@ template <class ReturnType>
|
||||||
|
|
||||||
// TODO(calabrese) std::launder
|
// TODO(calabrese) std::launder
|
||||||
// TODO(calabrese) constexpr
|
// TODO(calabrese) constexpr
|
||||||
|
// NOTE: DO NOT REMOVE the `inline` keyword as it is necessary to work around a
|
||||||
|
// MSVC bug. See https://github.com/abseil/abseil-cpp/issues/129 for details.
|
||||||
template <class Self, std::size_t I>
|
template <class Self, std::size_t I>
|
||||||
VariantAccessResult<I, Self> AccessUnion(Self&& self, SizeT<I> /*i*/) {
|
inline VariantAccessResult<I, Self> AccessUnion(Self&& self, SizeT<I> /*i*/) {
|
||||||
return reinterpret_cast<VariantAccessResult<I, Self>>(self);
|
return reinterpret_cast<VariantAccessResult<I, Self>>(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,7 +572,7 @@ struct VariantCoreAccess {
|
||||||
|
|
||||||
template <class Variant>
|
template <class Variant>
|
||||||
static void InitFrom(Variant& self, Variant&& other) { // NOLINT
|
static void InitFrom(Variant& self, Variant&& other) { // NOLINT
|
||||||
variant_internal::visit_indices<absl::variant_size<Variant>::value>(
|
VisitIndices<absl::variant_size<Variant>::value>::Run(
|
||||||
InitFromVisitor<Variant, Variant&&>{&self,
|
InitFromVisitor<Variant, Variant&&>{&self,
|
||||||
std::forward<Variant>(other)},
|
std::forward<Variant>(other)},
|
||||||
other.index());
|
other.index());
|
||||||
|
@ -1049,9 +1308,7 @@ class VariantStateBaseDestructorNontrivial : protected VariantStateBase<T...> {
|
||||||
VariantStateBaseDestructorNontrivial* self;
|
VariantStateBaseDestructorNontrivial* self;
|
||||||
};
|
};
|
||||||
|
|
||||||
void destroy() {
|
void destroy() { VisitIndices<sizeof...(T)>::Run(Destroyer{this}, index_); }
|
||||||
variant_internal::visit_indices<sizeof...(T)>(Destroyer{this}, index_);
|
|
||||||
}
|
|
||||||
|
|
||||||
~VariantStateBaseDestructorNontrivial() { destroy(); }
|
~VariantStateBaseDestructorNontrivial() { destroy(); }
|
||||||
|
|
||||||
|
@ -1087,8 +1344,7 @@ class VariantMoveBaseNontrivial : protected VariantStateBaseDestructor<T...> {
|
||||||
VariantMoveBaseNontrivial(VariantMoveBaseNontrivial&& other) noexcept(
|
VariantMoveBaseNontrivial(VariantMoveBaseNontrivial&& other) noexcept(
|
||||||
absl::conjunction<std::is_nothrow_move_constructible<T>...>::value)
|
absl::conjunction<std::is_nothrow_move_constructible<T>...>::value)
|
||||||
: Base(NoopConstructorTag()) {
|
: Base(NoopConstructorTag()) {
|
||||||
variant_internal::visit_indices<sizeof...(T)>(Construct{this, &other},
|
VisitIndices<sizeof...(T)>::Run(Construct{this, &other}, other.index_);
|
||||||
other.index_);
|
|
||||||
index_ = other.index_;
|
index_ = other.index_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1131,8 +1387,7 @@ class VariantCopyBaseNontrivial : protected VariantMoveBase<T...> {
|
||||||
|
|
||||||
VariantCopyBaseNontrivial(VariantCopyBaseNontrivial const& other)
|
VariantCopyBaseNontrivial(VariantCopyBaseNontrivial const& other)
|
||||||
: Base(NoopConstructorTag()) {
|
: Base(NoopConstructorTag()) {
|
||||||
variant_internal::visit_indices<sizeof...(T)>(Construct{this, &other},
|
VisitIndices<sizeof...(T)>::Run(Construct{this, &other}, other.index_);
|
||||||
other.index_);
|
|
||||||
index_ = other.index_;
|
index_ = other.index_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1166,7 +1421,7 @@ class VariantMoveAssignBaseNontrivial : protected VariantCopyBase<T...> {
|
||||||
operator=(VariantMoveAssignBaseNontrivial&& other) noexcept(
|
operator=(VariantMoveAssignBaseNontrivial&& other) noexcept(
|
||||||
absl::conjunction<std::is_nothrow_move_constructible<T>...,
|
absl::conjunction<std::is_nothrow_move_constructible<T>...,
|
||||||
std::is_nothrow_move_assignable<T>...>::value) {
|
std::is_nothrow_move_assignable<T>...>::value) {
|
||||||
variant_internal::visit_indices<sizeof...(T)>(
|
VisitIndices<sizeof...(T)>::Run(
|
||||||
VariantCoreAccess::MakeMoveAssignVisitor(this, &other), other.index_);
|
VariantCoreAccess::MakeMoveAssignVisitor(this, &other), other.index_);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
@ -1195,7 +1450,7 @@ class VariantCopyAssignBaseNontrivial : protected VariantMoveAssignBase<T...> {
|
||||||
|
|
||||||
VariantCopyAssignBaseNontrivial& operator=(
|
VariantCopyAssignBaseNontrivial& operator=(
|
||||||
const VariantCopyAssignBaseNontrivial& other) {
|
const VariantCopyAssignBaseNontrivial& other) {
|
||||||
variant_internal::visit_indices<sizeof...(T)>(
|
VisitIndices<sizeof...(T)>::Run(
|
||||||
VariantCoreAccess::MakeCopyAssignVisitor(this, other), other.index_);
|
VariantCoreAccess::MakeCopyAssignVisitor(this, other), other.index_);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
@ -1336,7 +1591,7 @@ struct Swap {
|
||||||
template <std::size_t Wi>
|
template <std::size_t Wi>
|
||||||
void operator()(SizeT<Wi> /*w_i*/) {
|
void operator()(SizeT<Wi> /*w_i*/) {
|
||||||
if (v->index() == Wi) {
|
if (v->index() == Wi) {
|
||||||
visit_indices<sizeof...(Types)>(SwapSameIndex<Types...>{v, w}, Wi);
|
VisitIndices<sizeof...(Types)>::Run(SwapSameIndex<Types...>{v, w}, Wi);
|
||||||
} else {
|
} else {
|
||||||
generic_swap();
|
generic_swap();
|
||||||
}
|
}
|
||||||
|
@ -1370,8 +1625,7 @@ struct VariantHashBase<Variant,
|
||||||
if (var.valueless_by_exception()) {
|
if (var.valueless_by_exception()) {
|
||||||
return 239799884;
|
return 239799884;
|
||||||
}
|
}
|
||||||
size_t result =
|
size_t result = VisitIndices<variant_size<Variant>::value>::Run(
|
||||||
variant_internal::visit_indices<variant_size<Variant>::value>(
|
|
||||||
PerformVisitation<VariantHashVisitor, const Variant&>{
|
PerformVisitation<VariantHashVisitor, const Variant&>{
|
||||||
std::forward_as_tuple(var), VariantHashVisitor{}},
|
std::forward_as_tuple(var), VariantHashVisitor{}},
|
||||||
var.index());
|
var.index());
|
||||||
|
|
|
@ -416,8 +416,8 @@ constexpr absl::add_pointer_t<const T> get_if(
|
||||||
template <typename Visitor, typename... Variants>
|
template <typename Visitor, typename... Variants>
|
||||||
variant_internal::VisitResult<Visitor, Variants...> visit(Visitor&& vis,
|
variant_internal::VisitResult<Visitor, Variants...> visit(Visitor&& vis,
|
||||||
Variants&&... vars) {
|
Variants&&... vars) {
|
||||||
return variant_internal::visit_indices<
|
return variant_internal::
|
||||||
variant_size<absl::decay_t<Variants>>::value...>(
|
VisitIndices<variant_size<absl::decay_t<Variants> >::value...>::Run(
|
||||||
variant_internal::PerformVisitation<Visitor, Variants...>{
|
variant_internal::PerformVisitation<Visitor, Variants...>{
|
||||||
std::forward_as_tuple(absl::forward<Variants>(vars)...),
|
std::forward_as_tuple(absl::forward<Variants>(vars)...),
|
||||||
absl::forward<Visitor>(vis)},
|
absl::forward<Visitor>(vis)},
|
||||||
|
@ -573,7 +573,7 @@ class variant<T0, Tn...> : private variant_internal::VariantBase<T0, Tn...> {
|
||||||
variant& operator=(T&& t) noexcept(
|
variant& operator=(T&& t) noexcept(
|
||||||
std::is_nothrow_assignable<Tj&, T>::value&&
|
std::is_nothrow_assignable<Tj&, T>::value&&
|
||||||
std::is_nothrow_constructible<Tj, T>::value) {
|
std::is_nothrow_constructible<Tj, T>::value) {
|
||||||
variant_internal::visit_indices<sizeof...(Tn) + 1>(
|
variant_internal::VisitIndices<sizeof...(Tn) + 1>::Run(
|
||||||
variant_internal::VariantCoreAccess::MakeConversionAssignVisitor(
|
variant_internal::VariantCoreAccess::MakeConversionAssignVisitor(
|
||||||
this, absl::forward<T>(t)),
|
this, absl::forward<T>(t)),
|
||||||
index());
|
index());
|
||||||
|
@ -682,7 +682,7 @@ class variant<T0, Tn...> : private variant_internal::VariantBase<T0, Tn...> {
|
||||||
// true and `is_nothrow_swappable()` is same as `std::is_trivial()`.
|
// true and `is_nothrow_swappable()` is same as `std::is_trivial()`.
|
||||||
void swap(variant& rhs) noexcept(
|
void swap(variant& rhs) noexcept(
|
||||||
absl::conjunction<std::is_trivial<T0>, std::is_trivial<Tn>...>::value) {
|
absl::conjunction<std::is_trivial<T0>, std::is_trivial<Tn>...>::value) {
|
||||||
return variant_internal::visit_indices<sizeof...(Tn) + 1>(
|
return variant_internal::VisitIndices<sizeof...(Tn) + 1>::Run(
|
||||||
variant_internal::Swap<T0, Tn...>{this, &rhs}, rhs.index());
|
variant_internal::Swap<T0, Tn...>{this, &rhs}, rhs.index());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -722,7 +722,7 @@ template <typename... Types>
|
||||||
constexpr variant_internal::RequireAllHaveEqualT<Types...> operator==(
|
constexpr variant_internal::RequireAllHaveEqualT<Types...> operator==(
|
||||||
const variant<Types...>& a, const variant<Types...>& b) {
|
const variant<Types...>& a, const variant<Types...>& b) {
|
||||||
return (a.index() == b.index()) &&
|
return (a.index() == b.index()) &&
|
||||||
variant_internal::visit_indices<sizeof...(Types)>(
|
variant_internal::VisitIndices<sizeof...(Types)>::Run(
|
||||||
variant_internal::EqualsOp<Types...>{&a, &b}, a.index());
|
variant_internal::EqualsOp<Types...>{&a, &b}, a.index());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -731,7 +731,7 @@ template <typename... Types>
|
||||||
constexpr variant_internal::RequireAllHaveNotEqualT<Types...> operator!=(
|
constexpr variant_internal::RequireAllHaveNotEqualT<Types...> operator!=(
|
||||||
const variant<Types...>& a, const variant<Types...>& b) {
|
const variant<Types...>& a, const variant<Types...>& b) {
|
||||||
return (a.index() != b.index()) ||
|
return (a.index() != b.index()) ||
|
||||||
variant_internal::visit_indices<sizeof...(Types)>(
|
variant_internal::VisitIndices<sizeof...(Types)>::Run(
|
||||||
variant_internal::NotEqualsOp<Types...>{&a, &b}, a.index());
|
variant_internal::NotEqualsOp<Types...>{&a, &b}, a.index());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -741,7 +741,7 @@ constexpr variant_internal::RequireAllHaveLessThanT<Types...> operator<(
|
||||||
const variant<Types...>& a, const variant<Types...>& b) {
|
const variant<Types...>& a, const variant<Types...>& b) {
|
||||||
return (a.index() != b.index())
|
return (a.index() != b.index())
|
||||||
? (a.index() + 1) < (b.index() + 1)
|
? (a.index() + 1) < (b.index() + 1)
|
||||||
: variant_internal::visit_indices<sizeof...(Types)>(
|
: variant_internal::VisitIndices<sizeof...(Types)>::Run(
|
||||||
variant_internal::LessThanOp<Types...>{&a, &b}, a.index());
|
variant_internal::LessThanOp<Types...>{&a, &b}, a.index());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -751,7 +751,7 @@ constexpr variant_internal::RequireAllHaveGreaterThanT<Types...> operator>(
|
||||||
const variant<Types...>& a, const variant<Types...>& b) {
|
const variant<Types...>& a, const variant<Types...>& b) {
|
||||||
return (a.index() != b.index())
|
return (a.index() != b.index())
|
||||||
? (a.index() + 1) > (b.index() + 1)
|
? (a.index() + 1) > (b.index() + 1)
|
||||||
: variant_internal::visit_indices<sizeof...(Types)>(
|
: variant_internal::VisitIndices<sizeof...(Types)>::Run(
|
||||||
variant_internal::GreaterThanOp<Types...>{&a, &b},
|
variant_internal::GreaterThanOp<Types...>{&a, &b},
|
||||||
a.index());
|
a.index());
|
||||||
}
|
}
|
||||||
|
@ -762,7 +762,7 @@ constexpr variant_internal::RequireAllHaveLessThanOrEqualT<Types...> operator<=(
|
||||||
const variant<Types...>& a, const variant<Types...>& b) {
|
const variant<Types...>& a, const variant<Types...>& b) {
|
||||||
return (a.index() != b.index())
|
return (a.index() != b.index())
|
||||||
? (a.index() + 1) < (b.index() + 1)
|
? (a.index() + 1) < (b.index() + 1)
|
||||||
: variant_internal::visit_indices<sizeof...(Types)>(
|
: variant_internal::VisitIndices<sizeof...(Types)>::Run(
|
||||||
variant_internal::LessThanOrEqualsOp<Types...>{&a, &b},
|
variant_internal::LessThanOrEqualsOp<Types...>{&a, &b},
|
||||||
a.index());
|
a.index());
|
||||||
}
|
}
|
||||||
|
@ -773,7 +773,7 @@ constexpr variant_internal::RequireAllHaveGreaterThanOrEqualT<Types...>
|
||||||
operator>=(const variant<Types...>& a, const variant<Types...>& b) {
|
operator>=(const variant<Types...>& a, const variant<Types...>& b) {
|
||||||
return (a.index() != b.index())
|
return (a.index() != b.index())
|
||||||
? (a.index() + 1) > (b.index() + 1)
|
? (a.index() + 1) > (b.index() + 1)
|
||||||
: variant_internal::visit_indices<sizeof...(Types)>(
|
: variant_internal::VisitIndices<sizeof...(Types)>::Run(
|
||||||
variant_internal::GreaterThanOrEqualsOp<Types...>{&a, &b},
|
variant_internal::GreaterThanOrEqualsOp<Types...>{&a, &b},
|
||||||
a.index());
|
a.index());
|
||||||
}
|
}
|
||||||
|
|
220
absl/types/variant_benchmark.cc
Normal file
220
absl/types/variant_benchmark.cc
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Unit tests for the variant template. The 'is' and 'IsEmpty' methods
|
||||||
|
// of variant are not explicitly tested because they are used repeatedly
|
||||||
|
// in building other tests. All other public variant methods should have
|
||||||
|
// explicit tests.
|
||||||
|
|
||||||
|
#include "absl/types/variant.h"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
#include "absl/utility/utility.h"
|
||||||
|
|
||||||
|
namespace absl {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template <std::size_t I>
|
||||||
|
struct VariantAlternative {
|
||||||
|
char member;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Indices>
|
||||||
|
struct VariantOfAlternativesImpl;
|
||||||
|
|
||||||
|
template <std::size_t... Indices>
|
||||||
|
struct VariantOfAlternativesImpl<absl::index_sequence<Indices...>> {
|
||||||
|
using type = absl::variant<VariantAlternative<Indices>...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t NumAlternatives>
|
||||||
|
using VariantOfAlternatives = typename VariantOfAlternativesImpl<
|
||||||
|
absl::make_index_sequence<NumAlternatives>>::type;
|
||||||
|
|
||||||
|
struct Empty {};
|
||||||
|
|
||||||
|
template <class... T>
|
||||||
|
void Ignore(T...) noexcept {}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Empty DoNotOptimizeAndReturnEmpty(T&& arg) noexcept {
|
||||||
|
benchmark::DoNotOptimize(arg);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VisitorApplier {
|
||||||
|
struct Visitor {
|
||||||
|
template <class... T>
|
||||||
|
void operator()(T&&... args) const noexcept {
|
||||||
|
Ignore(DoNotOptimizeAndReturnEmpty(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class... Vars>
|
||||||
|
void operator()(const Vars&... vars) const noexcept {
|
||||||
|
absl::visit(Visitor(), vars...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t NumIndices, std::size_t CurrIndex = NumIndices - 1>
|
||||||
|
struct MakeWithIndex {
|
||||||
|
using Variant = VariantOfAlternatives<NumIndices>;
|
||||||
|
|
||||||
|
static Variant Run(std::size_t index) {
|
||||||
|
return index == CurrIndex
|
||||||
|
? Variant(absl::in_place_index_t<CurrIndex>())
|
||||||
|
: MakeWithIndex<NumIndices, CurrIndex - 1>::Run(index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t NumIndices>
|
||||||
|
struct MakeWithIndex<NumIndices, 0> {
|
||||||
|
using Variant = VariantOfAlternatives<NumIndices>;
|
||||||
|
|
||||||
|
static Variant Run(std::size_t /*index*/) { return Variant(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t NumIndices, class Dimensions>
|
||||||
|
struct MakeVariantTuple;
|
||||||
|
|
||||||
|
template <class T, std::size_t /*I*/>
|
||||||
|
using always_t = T;
|
||||||
|
|
||||||
|
template <std::size_t NumIndices>
|
||||||
|
VariantOfAlternatives<NumIndices> MakeVariant(std::size_t dimension,
|
||||||
|
std::size_t index) {
|
||||||
|
return dimension == 0
|
||||||
|
? MakeWithIndex<NumIndices>::Run(index % NumIndices)
|
||||||
|
: MakeVariant<NumIndices>(dimension - 1, index / NumIndices);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t NumIndices, std::size_t... Dimensions>
|
||||||
|
struct MakeVariantTuple<NumIndices, absl::index_sequence<Dimensions...>> {
|
||||||
|
using VariantTuple =
|
||||||
|
std::tuple<always_t<VariantOfAlternatives<NumIndices>, Dimensions>...>;
|
||||||
|
|
||||||
|
static VariantTuple Run(int index) {
|
||||||
|
return std::make_tuple(MakeVariant<NumIndices>(Dimensions, index)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::size_t integral_pow(std::size_t base, std::size_t power) {
|
||||||
|
return power == 0 ? 1 : base * integral_pow(base, power - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t End, std::size_t I = 0>
|
||||||
|
struct VisitTestBody {
|
||||||
|
template <class Vars, class State>
|
||||||
|
static bool Run(Vars& vars, State& state) {
|
||||||
|
if (state.KeepRunning()) {
|
||||||
|
absl::apply(VisitorApplier(), vars[I]);
|
||||||
|
return VisitTestBody<End, I + 1>::Run(vars, state);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t End>
|
||||||
|
struct VisitTestBody<End, End> {
|
||||||
|
template <class Vars, class State>
|
||||||
|
static bool Run(Vars& /*vars*/, State& /*state*/) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Visit operations where branch prediction is likely to give a boost.
|
||||||
|
template <std::size_t NumIndices, std::size_t NumDimensions = 1>
|
||||||
|
void BM_RedundantVisit(benchmark::State& state) {
|
||||||
|
auto vars =
|
||||||
|
MakeVariantTuple<NumIndices, absl::make_index_sequence<NumDimensions>>::
|
||||||
|
Run(static_cast<std::size_t>(state.range(0)));
|
||||||
|
|
||||||
|
for (auto _ : state) { // NOLINT
|
||||||
|
benchmark::DoNotOptimize(vars);
|
||||||
|
absl::apply(VisitorApplier(), vars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Visit operations where branch prediction is unlikely to give a boost.
|
||||||
|
template <std::size_t NumIndices, std::size_t NumDimensions = 1>
|
||||||
|
void BM_Visit(benchmark::State& state) {
|
||||||
|
constexpr std::size_t num_possibilities =
|
||||||
|
integral_pow(NumIndices, NumDimensions);
|
||||||
|
|
||||||
|
using VariantTupleMaker =
|
||||||
|
MakeVariantTuple<NumIndices, absl::make_index_sequence<NumDimensions>>;
|
||||||
|
using Tuple = typename VariantTupleMaker::VariantTuple;
|
||||||
|
|
||||||
|
Tuple vars[num_possibilities];
|
||||||
|
for (std::size_t i = 0; i < num_possibilities; ++i)
|
||||||
|
vars[i] = VariantTupleMaker::Run(i);
|
||||||
|
|
||||||
|
while (VisitTestBody<num_possibilities>::Run(vars, state)) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Visitation
|
||||||
|
// Each visit is on a different variant with a different active alternative)
|
||||||
|
|
||||||
|
// Unary visit
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 1);
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 2);
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 3);
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 4);
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 5);
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 6);
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 7);
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 8);
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 16);
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 32);
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 64);
|
||||||
|
|
||||||
|
// Binary visit
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 1, 2);
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 2, 2);
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 3, 2);
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 4, 2);
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 5, 2);
|
||||||
|
|
||||||
|
// Ternary visit
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 1, 3);
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 2, 3);
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 3, 3);
|
||||||
|
|
||||||
|
// Quaternary visit
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 1, 4);
|
||||||
|
BENCHMARK_TEMPLATE(BM_Visit, 2, 4);
|
||||||
|
|
||||||
|
// Redundant Visitation
|
||||||
|
// Each visit consistently has the same alternative active
|
||||||
|
|
||||||
|
// Unary visit
|
||||||
|
BENCHMARK_TEMPLATE(BM_RedundantVisit, 1)->Arg(0);
|
||||||
|
BENCHMARK_TEMPLATE(BM_RedundantVisit, 2)->DenseRange(0, 1);
|
||||||
|
BENCHMARK_TEMPLATE(BM_RedundantVisit, 8)->DenseRange(0, 7);
|
||||||
|
|
||||||
|
// Binary visit
|
||||||
|
BENCHMARK_TEMPLATE(BM_RedundantVisit, 1, 2)->Arg(0);
|
||||||
|
BENCHMARK_TEMPLATE(BM_RedundantVisit, 2, 2)
|
||||||
|
->DenseRange(0, integral_pow(2, 2) - 1);
|
||||||
|
BENCHMARK_TEMPLATE(BM_RedundantVisit, 4, 2)
|
||||||
|
->DenseRange(0, integral_pow(4, 2) - 1);
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace absl
|
Loading…
Reference in a new issue