Changeset View
Changeset View
Standalone View
Standalone View
libcxx/trunk/benchmarks/CartesianBenchmarks.hpp
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
// | // | ||||
// The LLVM Compiler Infrastructure | // The LLVM Compiler Infrastructure | ||||
// | // | ||||
// This file is dual licensed under the MIT and the University of Illinois Open | // This file is dual licensed under the MIT and the University of Illinois Open | ||||
// Source Licenses. See LICENSE.TXT for details. | // Source Licenses. See LICENSE.TXT for details. | ||||
// | // | ||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
#include <string> | #include <string> | ||||
#include <tuple> | #include <tuple> | ||||
#include <type_traits> | #include <type_traits> | ||||
#include <vector> | |||||
#include "benchmark/benchmark.h" | #include "benchmark/benchmark.h" | ||||
#include "test_macros.h" | #include "test_macros.h" | ||||
namespace internal { | namespace internal { | ||||
template <class D, class E, size_t I> | template <class D, class E, size_t I> | ||||
struct EnumValue : std::integral_constant<E, static_cast<E>(I)> { | struct EnumValue : std::integral_constant<E, static_cast<E>(I)> { | ||||
static std::string name() { return std::string("_") + D::Names[I]; } | static std::string name() { return std::string("_") + D::Names[I]; } | ||||
}; | }; | ||||
template <class D, class E, size_t ...Idxs> | template <class D, class E, size_t ...Idxs> | ||||
constexpr auto makeEnumValueTuple(std::index_sequence<Idxs...>) { | constexpr auto makeEnumValueTuple(std::index_sequence<Idxs...>) { | ||||
return std::make_tuple(EnumValue<D, E, Idxs>{}...); | return std::make_tuple(EnumValue<D, E, Idxs>{}...); | ||||
} | } | ||||
template <class T> | template <class B> | ||||
static auto skip(int) -> decltype(T::skip()) { | static auto skip(const B& Bench, int) -> decltype(Bench.skip()) { | ||||
return T::skip(); | return Bench.skip(); | ||||
} | } | ||||
template <class T> | template <class B> | ||||
static bool skip(char) { | static auto skip(const B& Bench, char) { | ||||
return false; | return false; | ||||
} | } | ||||
template <template <class...> class B, class... U> | template <class B, class Args, size_t... Is> | ||||
void makeBenchmarkImpl(std::tuple<U...> t) { | void makeBenchmarkFromValuesImpl(const Args& A, std::index_sequence<Is...>) { | ||||
using T = B<U...>; | for (auto& V : A) { | ||||
if (!internal::skip<T>(0)) | B Bench{std::get<Is>(V)...}; | ||||
benchmark::RegisterBenchmark(T::name().c_str(), T::run); | if (!internal::skip(Bench, 0)) { | ||||
benchmark::RegisterBenchmark(Bench.name().c_str(), | |||||
[=](benchmark::State& S) { Bench.run(S); }); | |||||
} | |||||
} | |||||
} | |||||
template <class B, class... Args> | |||||
void makeBenchmarkFromValues(const std::vector<std::tuple<Args...> >& A) { | |||||
makeBenchmarkFromValuesImpl<B>(A, std::index_sequence_for<Args...>()); | |||||
} | |||||
template <template <class...> class B, class Args, class... U> | |||||
void makeBenchmarkImpl(const Args& A, std::tuple<U...> t) { | |||||
makeBenchmarkFromValues<B<U...> >(A); | |||||
} | } | ||||
template <template <class...> class B, class... U, class... T, class... Tuples> | template <template <class...> class B, class Args, class... U, | ||||
void makeBenchmarkImpl(std::tuple<U...>, std::tuple<T...>, Tuples... rest) { | class... T, class... Tuples> | ||||
(internal::makeBenchmarkImpl<B>(std::tuple<U..., T>(), rest...), ...); | void makeBenchmarkImpl(const Args& A, std::tuple<U...>, std::tuple<T...>, | ||||
Tuples... rest) { | |||||
(internal::makeBenchmarkImpl<B>(A, std::tuple<U..., T>(), rest...), ...); | |||||
} | |||||
template <class R, class T> | |||||
void allValueCombinations(R& Result, const T& Final) { | |||||
return Result.push_back(Final); | |||||
} | |||||
template <class R, class T, class V, class... Vs> | |||||
void allValueCombinations(R& Result, const T& Prev, const V& Value, | |||||
const Vs&... Values) { | |||||
for (const auto& E : Value) { | |||||
allValueCombinations(Result, std::tuple_cat(Prev, std::make_tuple(E)), | |||||
Values...); | |||||
} | |||||
} | } | ||||
} // namespace internal | } // namespace internal | ||||
// CRTP class that enables using enum types as a dimension for | // CRTP class that enables using enum types as a dimension for | ||||
// makeCartesianProductBenchmark below. | // makeCartesianProductBenchmark below. | ||||
// The type passed to `B` will be a std::integral_constant<E, e>, with the | // The type passed to `B` will be a std::integral_constant<E, e>, with the | ||||
// additional static function `name()` that returns the stringified name of the | // additional static function `name()` that returns the stringified name of the | ||||
// label. | // label. | ||||
// | // | ||||
// Eg: | // Eg: | ||||
// enum class MyEnum { A, B }; | // enum class MyEnum { A, B }; | ||||
// struct AllMyEnum : EnumValuesAsTuple<AllMyEnum, MyEnum, 2> { | // struct AllMyEnum : EnumValuesAsTuple<AllMyEnum, MyEnum, 2> { | ||||
// static constexpr absl::string_view Names[] = {"A", "B"}; | // static constexpr absl::string_view Names[] = {"A", "B"}; | ||||
// }; | // }; | ||||
template <class Derived, class EnumType, size_t NumLabels> | template <class Derived, class EnumType, size_t NumLabels> | ||||
using EnumValuesAsTuple = | using EnumValuesAsTuple = | ||||
decltype(internal::makeEnumValueTuple<Derived, EnumType>( | decltype(internal::makeEnumValueTuple<Derived, EnumType>( | ||||
std::make_index_sequence<NumLabels>{})); | std::make_index_sequence<NumLabels>{})); | ||||
// Instantiates B<T0, T1, ..., TN> where <Ti...> are the combinations in the | // Instantiates B<T0, T1, ..., TN> where <Ti...> are the combinations in the | ||||
// cartesian product of `Tuples...` | // cartesian product of `Tuples...`, and pass (arg0, ..., argN) as constructor | ||||
// arguments where `(argi...)` are the combination in the cartesian product of | |||||
// the runtime values of `A...`. | |||||
// B<T...> requires: | // B<T...> requires: | ||||
// - static std::string name(): The name of the benchmark. | // - std::string name(args...): The name of the benchmark. | ||||
// - static void run(benchmark::State&): The body of the benchmark. | // - void run(benchmark::State&, args...): The body of the benchmark. | ||||
// It can also optionally provide: | // It can also optionally provide: | ||||
// - static bool skip(): When `true`, skips the combination. Default is false. | // - bool skip(args...): When `true`, skips the combination. Default is false. | ||||
// | // | ||||
// Returns int to facilitate registration. The return value is unspecified. | // Returns int to facilitate registration. The return value is unspecified. | ||||
template <template <class...> class B, class... Tuples> | template <template <class...> class B, class... Tuples, class... Args> | ||||
int makeCartesianProductBenchmark() { | int makeCartesianProductBenchmark(const Args&... A) { | ||||
internal::makeBenchmarkImpl<B>(std::tuple<>(), Tuples()...); | std::vector<std::tuple<typename Args::value_type...> > V; | ||||
internal::allValueCombinations(V, std::tuple<>(), A...); | |||||
internal::makeBenchmarkImpl<B>(V, std::tuple<>(), Tuples()...); | |||||
return 0; | |||||
} | |||||
template <class B, class... Args> | |||||
int makeCartesianProductBenchmark(const Args&... A) { | |||||
std::vector<std::tuple<typename Args::value_type...> > V; | |||||
internal::allValueCombinations(V, std::tuple<>(), A...); | |||||
internal::makeBenchmarkFromValues<B>(V); | |||||
return 0; | return 0; | ||||
} | } | ||||
// When `opaque` is true, this function hides the runtime state of `value` from | // When `opaque` is true, this function hides the runtime state of `value` from | ||||
// the optimizer. | // the optimizer. | ||||
// It returns `value`. | // It returns `value`. | ||||
template <class T> | template <class T> | ||||
TEST_ALWAYS_INLINE inline T maybeOpaque(T value, bool opaque) { | TEST_ALWAYS_INLINE inline T maybeOpaque(T value, bool opaque) { | ||||
if (opaque) benchmark::DoNotOptimize(value); | if (opaque) benchmark::DoNotOptimize(value); | ||||
return value; | return value; | ||||
} | } | ||||