-
Notifications
You must be signed in to change notification settings - Fork 166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reverse mode asymptotically too slow O(N^14)? #332
Comments
Thank you for reporting this. I'm thinking about deprecating the number type |
Make sense. I really really like how easy it is to call and use this library. But the performance is unfortunately a deal breaker. fwiw, I tried using a simple reverse mode implementation based on https://github.com/Rookfighter/autodiff-cpp/blob/master/include/adcpp.h and it appears to hit the same performance explosion on the case above. So perhaps there's a common pitfall here. I'd love to understand what it is. Meanwhile, after a long battle with cmake I got https://mc-stan.org/math/ working. Here's an analogous benchmark: // THIS MUST BE INCLUDED BEFORE ANY EIGEN HEADERS
#include <stan/math.hpp>
#include <Eigen/Dense>
#include <iostream>
// tictoc
#include <chrono>
double tictoc()
{
double t = std::chrono::duration<double>(
std::chrono::system_clock::now().time_since_epoch()).count();
static double t0 = t;
double tdiff = t-t0;
t0 = t;
return tdiff;
}
template <typename T, int N>
T llt_func( Eigen::Matrix<T, N, 1> & x)
{
Eigen::Matrix<T, N, N> A;
for(int i = 0; i < N; i++)
{
for(int j = 0; j < N; j++)
{
A(i, j) = x(i)+x(j);
}
A(i,i) = 2.0*A(i,i);
}
Eigen::Matrix<T, N, 1> b = A.llt().solve(x);
T y = b.squaredNorm();
return y;
}
// stan::math::gradient only supports Eigen::Dynamic
template <typename T, int N>
T llt_func_helper( Eigen::Matrix<T, Eigen::Dynamic, 1> & _x)
{
Eigen::Matrix<T, N, 1> x = _x.template head<N>();
return llt_func<T,N>(x);
}
template <int N, int max_N>
void benchmark()
{
tictoc();
const int max_iter = 1000;
Eigen::Matrix<double, Eigen::Dynamic, 1> dydx(N);
double yt = 0;
for(int iter = 0;iter < max_iter;iter++)
{
Eigen::Matrix<double, Eigen::Dynamic, 1> x(N);
for(int i = 0; i < N; i++)
{
x(i) = i+1;
}
double y;
stan::math::gradient(llt_func_helper<stan::math::var,N>, x, y, dydx);
yt += y;
}
printf("%d %g \n",N,tictoc()/max_iter);
if constexpr (N<max_N)
{
benchmark<N+1,max_N>();
}
}
int main()
{
benchmark<1,30>();
return 0;
}
And it's performance seems excellent in comparison:
|
Following the tape-based implementation on https://rufflewind.com/2016-12-30/reverse-mode-automatic-differentiation, I was able to get performance on this test case that matches the stan-math trend (though it's still about 2× slower):
|
Apologies that I haven't simplified this example more:
On my machine this prints:
The forward evaluation could be O(N³) and that'd make finite differences O(N⁴), but these casual timings are well under the radar for that.
What stands out is fitting a slope to the reverse-mode
gradient
call which looks like N14.The
.llt().solve
seems to trigger some really bad complexity issues here.I'm sure forward mode would be fine in this case, but in my larger project I am really counting on reverse-mode.
The text was updated successfully, but these errors were encountered: