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只是一个例子,很多类型都有这个问题
}
- 要注意aliasing的问题(详见: Eigen: Aliasing),例如
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开始,会给出错误的结果
- 最好不要使用auto keyword(详见: Eigen: Common pitfalls),例如下面的代码都是有问题的。
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