Eigen常见的坑

背景

Eigen是C++的一个知名的header only的矩阵运算库。

最近组里的codebase要引入Eigen库。本来就是非常简单的NN的forward loop,我建议的是直接三重for循环手写矩阵乘法就好了。而组里同学还是坚持想用Eigen,所以我尝试总结一下Eigen常见的坑,以减少线上代码因为Eigen而出问题的概率。

Checklist

  • 编译的时候最好-DEIGEN_MPL2_ONLY(详见: Eigen)
    • 这是因为Eigen虽然大部分是MPL2 licensed的,但是还有少部分代码是LGPL licensed的,如果修改了其代码,则必须开源。
      • 这可能产生法律风险,而遭到法务部门的Complain
  • 最好redefine宏eigen_assert(详见: Eigen: Assertions)
    • 因为Eigen中默认eigen_assert会直接让程序CRASH,即使定义了宏NDEBUG
    • 而实际生产环境中,我们更需要的是log错误信息,然后采取兜底策略。
  • 要注意alignment的问题(详见: Explanation of the assertion on unaligned arrays),例如下面的代码都是有问题的,可能导致整个程序Crash。
    • 结构体含有Eigen类型的成员变量
class Foo {
  //...
  Eigen::Vector2d v; // 这个类型只是一个例子,很多类型都有问题
  //...
};
//...
Foo *foo = new Foo;
    • Eigen类型的变量被放到STL容器中
// Eigen::Matrix2f这个类型只是一个例子,很多类型都有问题
std::vector<Eigen::Matrix2f> my_vector;
struct my_class { ... Eigen::Matrix2f m; ... }; 
std::map<int, my_class> my_map;
    • Eigen类型的变量被按值传入一个函数
// Eigen::Vector4d只是一个例子,很多类型都有这个问题
void func(Eigen::Vector4d v);
    • 在栈上定义Eigen类型的变量(只有GCC4.4及以下版本 on Windows被发现有这个问题,例如MinGW or TDM-GCC)
void foo() {
  Eigen::Quaternionf q; // Eigen::Quaternionf只是一个例子,很多类型都有这个问题
}
mat = mat.transpose(); // mat会得到错误的结果

MatrixXf A(2,2), B(3,2);
B << 2, 0,  0, 3, 1, 1;
A << 2, 0, 0, -2;
A = (B * A).cwiseAbs(); // 从Eigen3.3开始,会给出错误的结果
MatrixXd A, B;
auto C = A*B;
for(...) { ... w = C * v;  ...} // 会导致 A*B多次计算

auto C = ((A+B).eval()).transpose();
// do something with C,大概率会segment fault

VectorXd u, v;
auto C = u + (A*v).normalized();
// do something with C,大概率会segment fault
  • 对于原本多线程的程序,最好定义宏EIGEN_DONT_PARALLELIZE (详见: Eigen and multi-threading
    • 因为如果编译参数传入-fopenmp,则对于矩阵乘法等运算可能会自动多线程运行,而打破系统原本的线程池。

写在后面

这些坑我只是用了几个小时的时间浏览Eigen的文档得到的,由于没有详细看Eigen的源代码,所以还可能存在其他的坑。如果有人碰到过其他的坑,求在留言中帮忙补充。

编辑于 2017-12-25 13:08