blob: 74c334f97b239415df9d87c1d78fb0d5bf814f9f [file]
// Benchmarks for Eigen AutoDiff module.
// Compares AutoDiff Jacobian computation against NumericalDiff and hand-coded Jacobians.
#include <benchmark/benchmark.h>
#include <Eigen/Core>
#include <unsupported/Eigen/AutoDiff>
#include <unsupported/Eigen/NumericalDiff>
using namespace Eigen;
// --- Small functor: Rosenbrock-like (2 inputs -> 2 outputs) ---
struct SmallFunctor {
typedef Matrix<double, 2, 1> InputType;
typedef Matrix<double, 2, 1> ValueType;
typedef Matrix<double, 2, 2> JacobianType;
enum { InputsAtCompileTime = 2, ValuesAtCompileTime = 2 };
template <typename T>
void operator()(const Matrix<T, 2, 1>& x, Matrix<T, 2, 1>* v) const {
(*v)(0) = T(1) - x(0);
(*v)(1) = T(10) * (x(1) - x(0) * x(0));
}
};
// --- Medium functor: chain of operations (6 inputs -> 6 outputs) ---
struct MediumFunctor {
typedef Matrix<double, 6, 1> InputType;
typedef Matrix<double, 6, 1> ValueType;
typedef Matrix<double, 6, 6> JacobianType;
enum { InputsAtCompileTime = 6, ValuesAtCompileTime = 6 };
template <typename T>
void operator()(const Matrix<T, 6, 1>& x, Matrix<T, 6, 1>* v) const {
(*v)(0) = sin(x(0)) * cos(x(1)) + x(2) * x(2);
(*v)(1) = exp(x(1) * T(0.1)) + x(3);
(*v)(2) = x(0) * x(2) - x(4) * x(5);
(*v)(3) = sqrt(x(3) * x(3) + T(1)) + x(0);
(*v)(4) = x(4) * x(4) + x(5) * x(5) + x(0) * x(1);
(*v)(5) = log(x(2) * x(2) + T(1)) + x(3) * x(4);
}
};
// --- Dynamic-size functor (N inputs -> N outputs) ---
struct DynamicFunctor {
typedef Matrix<double, Dynamic, 1> InputType;
typedef Matrix<double, Dynamic, 1> ValueType;
typedef Matrix<double, Dynamic, Dynamic> JacobianType;
const int n_;
DynamicFunctor(int n) : n_(n) {}
enum { InputsAtCompileTime = Dynamic, ValuesAtCompileTime = Dynamic };
int inputs() const { return n_; }
int values() const { return n_; }
template <typename T>
void operator()(const Matrix<T, Dynamic, 1>& x, Matrix<T, Dynamic, 1>* v) const {
v->resize(n_);
(*v)(0) = T(1) - x(0);
for (int i = 1; i < n_; ++i) {
(*v)(i) = T(10) * (x(i) - x(i - 1) * x(i - 1));
}
}
};
// Wrapper for NumericalDiff compatibility.
struct SmallFunctorND : SmallFunctor {
typedef double Scalar;
int inputs() const { return 2; }
int values() const { return 2; }
int operator()(const InputType& x, ValueType& v) const {
SmallFunctor::operator()(x, &v);
return 0;
}
};
struct MediumFunctorND : MediumFunctor {
typedef double Scalar;
int inputs() const { return 6; }
int values() const { return 6; }
int operator()(const InputType& x, ValueType& v) const {
MediumFunctor::operator()(x, &v);
return 0;
}
};
// --- AutoDiff Jacobian benchmarks ---
template <typename Functor>
static void BM_AutoDiffJacobian(benchmark::State& state, Functor func) {
AutoDiffJacobian<Functor> adf(func);
typename Functor::InputType x = Functor::InputType::Random();
typename Functor::ValueType v;
typename Functor::JacobianType jac;
for (auto _ : state) {
adf(x, &v, &jac);
benchmark::DoNotOptimize(jac.data());
benchmark::ClobberMemory();
}
}
// --- Dynamic AutoDiff Jacobian ---
static void BM_AutoDiffJacobian_Dynamic(benchmark::State& state) {
int n = state.range(0);
DynamicFunctor func(n);
AutoDiffJacobian<DynamicFunctor> adf(func);
VectorXd x = VectorXd::Random(n);
VectorXd v(n);
MatrixXd jac(n, n);
for (auto _ : state) {
adf(x, &v, &jac);
benchmark::DoNotOptimize(jac.data());
benchmark::ClobberMemory();
}
}
// --- NumericalDiff benchmarks ---
template <typename Functor>
static void BM_NumericalDiffJacobian(benchmark::State& state, Functor func) {
NumericalDiff<Functor> ndf(func);
typename Functor::InputType x = Functor::InputType::Random();
typename Functor::JacobianType jac;
for (auto _ : state) {
ndf.df(x, jac);
benchmark::DoNotOptimize(jac.data());
benchmark::ClobberMemory();
}
}
// --- Hand-coded Jacobian (Rosenbrock) for comparison ---
static void BM_HandCoded_Small(benchmark::State& state) {
Vector2d x = Vector2d::Random();
Matrix2d jac;
for (auto _ : state) {
jac(0, 0) = -1;
jac(0, 1) = 0;
jac(1, 0) = -20 * x(0);
jac(1, 1) = 10;
benchmark::DoNotOptimize(jac.data());
benchmark::ClobberMemory();
}
}
// --- Scalar AutoDiff evaluation (no Jacobian, just forward pass) ---
static void BM_AutoDiffScalar_Eval(benchmark::State& state) {
int n = state.range(0);
using ADScalar = AutoDiffScalar<VectorXd>;
VectorXd x = VectorXd::Random(n);
for (auto _ : state) {
ADScalar sum(0.0, VectorXd::Zero(n));
for (int i = 0; i < n; ++i) {
ADScalar xi(x(i), n, i);
sum += xi * xi + sin(xi);
}
benchmark::DoNotOptimize(sum.value());
benchmark::DoNotOptimize(sum.derivatives().data());
benchmark::ClobberMemory();
}
}
BENCHMARK_CAPTURE(BM_AutoDiffJacobian, Small, SmallFunctor());
BENCHMARK_CAPTURE(BM_AutoDiffJacobian, Medium, MediumFunctor());
BENCHMARK(BM_AutoDiffJacobian_Dynamic)->Arg(2)->Arg(6)->Arg(20)->Arg(50)->Arg(100);
BENCHMARK_CAPTURE(BM_NumericalDiffJacobian, Small, SmallFunctorND());
BENCHMARK_CAPTURE(BM_NumericalDiffJacobian, Medium, MediumFunctorND());
BENCHMARK(BM_HandCoded_Small);
BENCHMARK(BM_AutoDiffScalar_Eval)->Arg(2)->Arg(6)->Arg(20)->Arg(50)->Arg(100);