I just spent a while tracking down a rather surprising performance bug in my code.
Here's a minimal example:
#include <Eigen/Dense>
#include <iostream>
int simple_size(const Eigen::MatrixXi & Z)
{
return Z.size();
}
template <typename T> int templated_size(const Eigen::MatrixBase<T> & Y)
{
return simple_size(Y);
}
int main(int argc, const char * argv[])
{
const int s = 40000;
Eigen::MatrixXi X = Eigen::MatrixXi::Zero(40000,40000);
std::cout<<"Compare:"<<std::endl;
std::cout<<(X.size() ?"done":"")<<std::endl;
std::cout<<(simple_size(X) ?"done":"")<<std::endl;
std::cout<<(templated_size(X)?"done":"")<<std::endl;
}
Running this, it will show that the last call to templated_size
is taking way too long. Inspection will show that a copy of Y
is being created to create a Eigen::MatrixXi &
reference.
Now, clearly it's poor design to call a function expecting a Eigen::MatrixXi &
reference with a generic templated type Eigen::MatrixBase<T> &
, but unfortunately this happens quite often with legacy libigl functions. My expectation was that since T
is Eigen::MatrixXi
in this case a simple reference would be passed.
It's worth noting that const
is actually creating/hiding the problem. Because simple_size
takes a const reference, the compiler is happy to construct a Eigen::MatrixXi
on the fly to create a valid reference. Without the const
s the compiler stops at an error.