mirror of
https://gitlab.com/libeigen/eigen.git
synced 2026-04-10 11:34:33 +08:00
178 lines
5.2 KiB
C++
178 lines
5.2 KiB
C++
// 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);
|