blob: f3ce87509bee1fcfa9066501f945d030a80bf3f8 [file]
// SPDX-FileCopyrightText: The Eigen Authors
// SPDX-License-Identifier: MPL-2.0
#include <benchmark/benchmark.h>
#include <Eigen/Core>
#include <cmath>
#include <complex>
#include <limits>
#include <vector>
using namespace Eigen;
// Integer powers by double-word repeated squaring, against the log/exp path of generic_pow that they replace for
// large exponents. The "tiny" label puts the results about 2^-100 (float) or 2^-960 (double): normal numbers whose
// double-word residuals would be subnormal without the scaling of the running power.
template <typename T>
Array<T, Dynamic, 1> real_bases(Index size, int exponent, bool tiny) {
Array<T, Dynamic, 1> x = Array<T, Dynamic, 1>::Random(size) * T(0.5) + T(1.25); // [0.75, 1.75]
if (tiny) {
int target = -(std::numeric_limits<T>::max_exponent * 3) / 4;
x *= T(std::ldexp(1.0, target / std::abs(exponent)));
if (exponent < 0) x = x.inverse();
}
return x;
}
template <typename T>
static void BM_UnaryPowInt(benchmark::State& state) {
Index size = 4097;
int exponent = int(state.range(0));
bool tiny = state.range(1) != 0;
Array<T, Dynamic, 1> x = real_bases<T>(size, exponent, tiny), y(size);
for (auto _ : state) {
benchmark::DoNotOptimize(x.data());
y = x.pow(exponent);
benchmark::DoNotOptimize(y.data());
benchmark::ClobberMemory();
}
state.SetItemsProcessed(state.iterations() * size);
state.SetLabel(tiny ? "tiny" : "ordinary");
}
template <typename T>
static void BM_UnaryPowReal(benchmark::State& state) {
Index size = 4097;
int exponent = int(state.range(0));
bool tiny = state.range(1) != 0;
Array<T, Dynamic, 1> x = real_bases<T>(size, exponent, tiny), y(size);
for (auto _ : state) {
benchmark::DoNotOptimize(x.data());
y = x.pow(T(exponent));
benchmark::DoNotOptimize(y.data());
benchmark::ClobberMemory();
}
state.SetItemsProcessed(state.iterations() * size);
state.SetLabel(tiny ? "tiny" : "ordinary");
}
// The vectorized log/exp power on its own.
template <typename T>
static void BM_GenericPow(benchmark::State& state) {
using Packet = typename internal::packet_traits<T>::type;
constexpr Index kPacketSize = internal::unpacket_traits<Packet>::size;
Index size = 4096;
int exponent = int(state.range(0));
bool tiny = state.range(1) != 0;
Array<T, Dynamic, 1> x = real_bases<T>(size, exponent, tiny), y(size);
Packet packet_exponent = internal::pset1<Packet>(T(exponent));
for (auto _ : state) {
benchmark::DoNotOptimize(x.data());
for (Index i = 0; i < size; i += kPacketSize)
internal::pstoreu(y.data() + i, internal::generic_pow(internal::ploadu<Packet>(x.data() + i), packet_exponent));
benchmark::DoNotOptimize(y.data());
benchmark::ClobberMemory();
}
state.SetItemsProcessed(state.iterations() * size);
state.SetLabel(tiny ? "tiny" : "ordinary");
}
// Fixed sizes with a literal exponent: the assignment unrolls and the exponent can fold into straight-line code.
template <typename T, int Size, int Exponent>
static void BM_UnaryPowFixed(benchmark::State& state) {
Array<T, Size, 1> x = Array<T, Size, 1>::Random() * T(0.5) + T(1.25), y;
for (auto _ : state) {
benchmark::DoNotOptimize(x.data());
y = x.pow(Exponent);
benchmark::DoNotOptimize(y.data());
benchmark::ClobberMemory();
}
state.SetItemsProcessed(state.iterations() * Size);
}
template <typename T>
Array<std::complex<T>, Dynamic, 1> complex_bases(Index size) {
using C = std::complex<T>;
Array<C, Dynamic, 1> z = Array<C, Dynamic, 1>::Random(size);
return z.unaryExpr([](const C& v) { return v / std::abs(v) * T(1.05); });
}
template <typename T>
static void BM_ComplexPowInt(benchmark::State& state) {
Index size = 4097;
int exponent = int(state.range(0));
Array<std::complex<T>, Dynamic, 1> z = complex_bases<T>(size), y(size);
for (auto _ : state) {
benchmark::DoNotOptimize(z.data());
y = z.pow(exponent);
benchmark::DoNotOptimize(y.data());
benchmark::ClobberMemory();
}
state.SetItemsProcessed(state.iterations() * size);
}
// Complex bases with a real exponent take the scalar path.
template <typename T>
static void BM_ComplexPowReal(benchmark::State& state) {
Index size = 4097;
int exponent = int(state.range(0));
Array<std::complex<T>, Dynamic, 1> z = complex_bases<T>(size), y(size);
for (auto _ : state) {
benchmark::DoNotOptimize(z.data());
y = z.pow(T(exponent));
benchmark::DoNotOptimize(y.data());
benchmark::ClobberMemory();
}
state.SetItemsProcessed(state.iterations() * size);
}
template <typename T>
static void BM_StdComplexPow(benchmark::State& state) {
Index size = 4097;
int exponent = int(state.range(0));
Array<std::complex<T>, Dynamic, 1> z = complex_bases<T>(size), y(size);
for (auto _ : state) {
benchmark::DoNotOptimize(z.data());
for (Index i = 0; i < size; ++i) y(i) = std::pow(z(i), exponent);
benchmark::DoNotOptimize(y.data());
benchmark::ClobberMemory();
}
state.SetItemsProcessed(state.iterations() * size);
}
const std::vector<int64_t> kExponents = {2, 3, 8, 100, 1000, 1 << 20, -2, -3, -8, -100};
BENCHMARK_TEMPLATE(BM_UnaryPowInt, float)->ArgsProduct({kExponents, {0, 1}});
BENCHMARK_TEMPLATE(BM_UnaryPowInt, double)->ArgsProduct({kExponents, {0, 1}});
BENCHMARK_TEMPLATE(BM_UnaryPowReal, float)->ArgsProduct({kExponents, {0, 1}});
BENCHMARK_TEMPLATE(BM_UnaryPowReal, double)->ArgsProduct({kExponents, {0, 1}});
BENCHMARK_TEMPLATE(BM_GenericPow, float)->ArgsProduct({kExponents, {0, 1}});
BENCHMARK_TEMPLATE(BM_GenericPow, double)->ArgsProduct({kExponents, {0, 1}});
BENCHMARK_TEMPLATE(BM_UnaryPowFixed, float, 8, 3);
BENCHMARK_TEMPLATE(BM_UnaryPowFixed, float, 8, 8);
BENCHMARK_TEMPLATE(BM_UnaryPowFixed, float, 64, 3);
BENCHMARK_TEMPLATE(BM_UnaryPowFixed, float, 64, 8);
BENCHMARK_TEMPLATE(BM_UnaryPowFixed, float, 64, 100);
BENCHMARK_TEMPLATE(BM_UnaryPowFixed, double, 4, 3);
BENCHMARK_TEMPLATE(BM_UnaryPowFixed, double, 4, 8);
BENCHMARK_TEMPLATE(BM_UnaryPowFixed, double, 64, 3);
BENCHMARK_TEMPLATE(BM_UnaryPowFixed, double, 64, 8);
BENCHMARK_TEMPLATE(BM_UnaryPowFixed, double, 64, 100);
BENCHMARK_TEMPLATE(BM_ComplexPowInt, float)->Args({2})->Args({3})->Args({8})->Args({100})->Args({-2})->Args({-8});
BENCHMARK_TEMPLATE(BM_ComplexPowInt, double)->Args({2})->Args({3})->Args({8})->Args({100})->Args({-2})->Args({-8});
BENCHMARK_TEMPLATE(BM_ComplexPowReal, float)->Args({2})->Args({3})->Args({8})->Args({100})->Args({-2})->Args({-8});
BENCHMARK_TEMPLATE(BM_ComplexPowReal, double)->Args({2})->Args({3})->Args({8})->Args({100})->Args({-2})->Args({-8});
BENCHMARK_TEMPLATE(BM_StdComplexPow, float)->Args({2})->Args({3})->Args({8})->Args({100})->Args({-2})->Args({-8});
BENCHMARK_TEMPLATE(BM_StdComplexPow, double)->Args({2})->Args({3})->Args({8})->Args({100})->Args({-2})->Args({-8});