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 {n //...n Eigen::Vector2d v; // 這個類型只是一個例子,很多類型都有問題n //...n};n//...nFoo *foo = new Foo;n
- Eigen類型的變數被放到STL容器中
// Eigen::Matrix2f這個類型只是一個例子,很多類型都有問題nstd::vector<Eigen::Matrix2f> my_vector;nstruct my_class { ... Eigen::Matrix2f m; ... }; nstd::map<int, my_class> my_map;n
- Eigen類型的變數被按值傳入一個函數
// Eigen::Vector4d只是一個例子,很多類型都有這個問題nvoid func(Eigen::Vector4d v);n
- 在棧上定義Eigen類型的變數(只有GCC4.4及以下版本 on Windows被發現有這個問題,例如MinGW or TDM-GCC)
void foo() {n Eigen::Quaternionf q; // Eigen::Quaternionf只是一個例子,很多類型都有這個問題n}n
- 要注意aliasing的問題(詳見: Eigen: Aliasing),例如
mat = mat.transpose(); // mat會得到錯誤的結果nnMatrixXf A(2,2), B(3,2);nB << 2, 0, 0, 3, 1, 1;nA << 2, 0, 0, -2;nA = (B * A).cwiseAbs(); // 從Eigen3.3開始,會給出錯誤的結果n
- 最好不要使用auto keyword(詳見: Eigen: Common pitfalls),例如下面的代碼都是有問題的。
MatrixXd A, B;nauto C = A*B;nfor(...) { ... w = C * v; ...} // 會導致 A*B多次計算nnauto C = ((A+B).eval()).transpose();n// do something with C,大概率會segment faultnnVectorXd u, v;nauto C = u + (A*v).normalized();n// do something with C,大概率會segment faultn
- 對於原本多線程的程序,最好定義宏EIGEN_DONT_PARALLELIZE (詳見: Eigen and multi-threading)
- 因為如果編譯參數傳入-fopenmp,則對於矩陣乘法等運算可能會自動多線程運行,而打破系統原本的線程池。
寫在後面
這些坑我只是用了幾個小時的時間瀏覽Eigen的文檔得到的,由於沒有詳細看Eigen的源代碼,所以還可能存在其他的坑。如果有人碰到過其他的坑,求在留言中幫忙補充。
推薦閱讀: