blob: 6a373dc59678c57296ace6ed3bfd3e6a35a2e03b [file]
// SPDX-FileCopyrightText: The Eigen Authors
// SPDX-License-Identifier: MPL-2.0
// all(), any(), allFinite(), isZero() and isApproxToConstant(). The plain cases are full scans: no coefficient decides
// the result early. The "/first" cases are decided by coefficient 0, which measures the reads past the deciding one.
// Dynamic sizes hide the size and data pointer from the compiler on every iteration, as a caller whose sizes vary
// would; fixed sizes and a block of a matrix cover the other traversals.
//
// Each binary measures one family, selected by EIGEN_BENCH_PREDICATE_SCALAR and EIGEN_BENCH_PREDICATE_OP (dynamic),
// EIGEN_BENCH_PREDICATE_XPR and EIGEN_BENCH_PREDICATE_OP (fixed), or neither (block).
#include <benchmark/benchmark.h>
#include <Eigen/Core>
#include <cstdint>
#include <limits>
#include <string>
namespace Eigen {
namespace {
using std::int16_t;
enum Op { All, Any, AllFinite, IsZero, ApproxConstant };
template <typename T, Op op>
T fill() {
return op == Any || op == IsZero ? T(0) : op == ApproxConstant ? T(2) : T(1);
}
// A coefficient that decides the result on its own.
template <typename T, Op op>
T decider() {
return op == All ? T(0) : op == AllFinite ? std::numeric_limits<T>::infinity() : op == ApproxConstant ? T(3) : T(1);
}
template <Op op, typename Xpr>
bool apply(const Xpr& x) {
using Scalar = typename Xpr::Scalar;
switch (op) {
case All:
return x.all();
case Any:
return x.any();
case AllFinite:
return x.allFinite();
case IsZero:
return x.isZero();
default:
return x.isApproxToConstant(Scalar(2));
}
}
template <typename T, Op op, bool First>
void BM_Dynamic(benchmark::State& state) {
const Index n = state.range(0);
ArrayX<T> a = ArrayX<T>::Constant(n, fill<T, op>());
if (First) a(0) = decider<T, op>();
for (auto _ : state) {
Index m = n;
benchmark::DoNotOptimize(m);
const T* p = a.data();
benchmark::DoNotOptimize(p);
bool r = apply<op>(Map<const ArrayX<T>>(p, m));
benchmark::DoNotOptimize(r);
}
state.SetItemsProcessed(state.iterations() * n);
}
template <typename Xpr, Op op>
void BM_Fixed(benchmark::State& state) {
Xpr a = Xpr::Constant(fill<typename Xpr::Scalar, op>());
for (auto _ : state) {
benchmark::DoNotOptimize(a);
bool r = apply<op>(a);
benchmark::DoNotOptimize(r);
}
}
// isZero() on the top rows of a 64-column matrix: one inner vector per column, no linear access.
void BM_BlockIsZero(benchmark::State& state) {
const Index n = state.range(0);
MatrixXf m = MatrixXf::Zero(n + 3, 64);
for (auto _ : state) {
Index k = n;
benchmark::DoNotOptimize(k);
benchmark::DoNotOptimize(m.data());
bool r = m.topRows(k).isZero();
benchmark::DoNotOptimize(r);
}
state.SetItemsProcessed(state.iterations() * n * 64);
}
using Array16f = Array<float, 16, 1>;
using Array33f = Array<float, 33, 1>;
const char* const opNames[] = {"all", "any", "allFinite", "isZero", "isApproxToConstant"};
#if defined(EIGEN_BENCH_PREDICATE_SCALAR)
const bool registered = [] {
using T = EIGEN_BENCH_PREDICATE_SCALAR;
constexpr Op op = EIGEN_BENCH_PREDICATE_OP;
const std::string name = std::string(opNames[op]) + "<" EIGEN_MAKESTRING(EIGEN_BENCH_PREDICATE_SCALAR) ">";
benchmark::RegisterBenchmark(name.c_str(), BM_Dynamic<T, op, false>)
->DenseRange(1, 16)
->Arg(32)
->Arg(64)
->Arg(1024)
->Arg(16384);
benchmark::RegisterBenchmark((name + "/first").c_str(), BM_Dynamic<T, op, true>)
->Arg(16)
->Arg(64)
->Arg(256)
->Arg(1024)
->Arg(16384);
return true;
}();
#elif defined(EIGEN_BENCH_PREDICATE_XPR)
const bool registered = [] {
constexpr Op op = EIGEN_BENCH_PREDICATE_OP;
const std::string name = std::string(opNames[op]) + "<" EIGEN_MAKESTRING(EIGEN_BENCH_PREDICATE_XPR) ">";
benchmark::RegisterBenchmark(name.c_str(), BM_Fixed<EIGEN_BENCH_PREDICATE_XPR, op>);
return true;
}();
#else
const bool registered = [] {
benchmark::RegisterBenchmark("blockIsZero<float>", BM_BlockIsZero)->Arg(4)->Arg(16)->Arg(64)->Arg(256);
return true;
}();
#endif
} // namespace
} // namespace Eigen