// Benchmarks for Eigen AutoDiff module. // Compares AutoDiff Jacobian computation against NumericalDiff and hand-coded Jacobians. #include #include #include #include using namespace Eigen; // --- Small functor: Rosenbrock-like (2 inputs -> 2 outputs) --- struct SmallFunctor { typedef Matrix InputType; typedef Matrix ValueType; typedef Matrix JacobianType; enum { InputsAtCompileTime = 2, ValuesAtCompileTime = 2 }; template void operator()(const Matrix& x, Matrix* 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 InputType; typedef Matrix ValueType; typedef Matrix JacobianType; enum { InputsAtCompileTime = 6, ValuesAtCompileTime = 6 }; template void operator()(const Matrix& x, Matrix* 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 InputType; typedef Matrix ValueType; typedef Matrix 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 void operator()(const Matrix& x, Matrix* 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 static void BM_AutoDiffJacobian(benchmark::State& state, Functor func) { AutoDiffJacobian 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 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 static void BM_NumericalDiffJacobian(benchmark::State& state, Functor func) { NumericalDiff 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 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);