// 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 // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // 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 #include #include #include #include "benchmark/benchmark.h" #include "absl/utility/utility.h" namespace absl { namespace { template struct VariantAlternative { char member; }; template struct VariantOfAlternativesImpl; template struct VariantOfAlternativesImpl> { using type = absl::variant...>; }; template using VariantOfAlternatives = typename VariantOfAlternativesImpl< absl::make_index_sequence>::type; struct Empty {}; template void Ignore(T...) noexcept {} template Empty DoNotOptimizeAndReturnEmpty(T&& arg) noexcept { benchmark::DoNotOptimize(arg); return {}; } struct VisitorApplier { struct Visitor { template void operator()(T&&... args) const noexcept { Ignore(DoNotOptimizeAndReturnEmpty(args)...); } }; template void operator()(const Vars&... vars) const noexcept { absl::visit(Visitor(), vars...); } }; template struct MakeWithIndex { using Variant = VariantOfAlternatives; static Variant Run(std::size_t index) { return index == CurrIndex ? Variant(absl::in_place_index_t()) : MakeWithIndex::Run(index); } }; template struct MakeWithIndex { using Variant = VariantOfAlternatives; static Variant Run(std::size_t /*index*/) { return Variant(); } }; template struct MakeVariantTuple; template using always_t = T; template VariantOfAlternatives MakeVariant(std::size_t dimension, std::size_t index) { return dimension == 0 ? MakeWithIndex::Run(index % NumIndices) : MakeVariant(dimension - 1, index / NumIndices); } template struct MakeVariantTuple> { using VariantTuple = std::tuple, Dimensions>...>; static VariantTuple Run(int index) { return std::make_tuple(MakeVariant(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 struct VisitTestBody { template static bool Run(Vars& vars, State& state) { if (state.KeepRunning()) { absl::apply(VisitorApplier(), vars[I]); return VisitTestBody::Run(vars, state); } return false; } }; template struct VisitTestBody { template static bool Run(Vars& /*vars*/, State& /*state*/) { return true; } }; // Visit operations where branch prediction is likely to give a boost. template void BM_RedundantVisit(benchmark::State& state) { auto vars = MakeVariantTuple>:: Run(static_cast(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 void BM_Visit(benchmark::State& state) { constexpr std::size_t num_possibilities = integral_pow(NumIndices, NumDimensions); using VariantTupleMaker = MakeVariantTuple>; 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::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