有的時候寫出來的程序慢也許並不是算法有問題,而是用的庫比較慢;也可能並不是庫本身慢,而只是你的寫法不夠高效。在經歷了無數次令人蛋疼的等待後,我決定比較一下這幾個所謂的高效的線性代數庫(OpenCV雖然目標是計算機視覺,但也提供了比較豐富的代數計算能力),看看它們的性能到底怎麼樣。 有人已經做過類似的事情了,比如 OpenCV vs. Armadillo vs. Eigen on Linux revisited,這哥們比較了這幾個庫在各種矩陣運算方面的效率,總結的比較齊全。但是,在計算機視覺領域這些還不夠,比如經常使用的相似性度量(Similarity Measure)的計算。當然後很多種方法,這裡就考慮最基本的SAD(Sum of Absolute Difference)方法吧,簡單來說就是把兩個矩陣(或者向量)相減,求個絕對值,再加起來。這個計算看起來挺簡單的,不過比較的結果令我比較意外。 先把代碼貼出來吧。 [cpp] // PerformanceTest.h #pragma warning(disable:4344) #define DEIGEN_NO_DEBUG #define DNDEBUG #include <emmintrin.h> #include <opencv.hpp> #include <vector> #include <iostream> #include <armadillo> #include <Eigen/Dense> #include "Timer.h" using namespace std; [cpp] // PerformanceTest.cpp #include "PerformanceTest.h" int main(void) { Timer timer; // timer double elapsedTime; // time in millisecond double res; // SAD value int i; // loop variable float bnd = 1e5; // loop times // Armadillo arma::mat armaA(4, 1); arma::mat armaB(4, 1); timer.start(); for (i = 0; i < bnd; ++i) { res = arma::accu(arma::abs(armaA - armaB)); //res = 0; //for (int idx = 0; idx < 4; ++idx) //{ // res += abs(armaA(idx, 0) - armaB(idx, 0)); //} } elapsedTime = timer.getElapsedTimeInMilliSec(); cout<<"arma time : "<<elapsedTime<<" ms"<<endl; // Eigen Eigen::Vector4d eiA; Eigen::Vector4d eiB; Eigen::Vector4d eiC; timer.start(); for (i = 0; i < bnd; ++i) { res = (eiA - eiB).cwiseAbs().sum(); //res = 0; //for (int idx = 0; idx < 4; ++idx) //{ // res += abs(eiA(idx,0) - eiB(idx, 0)); //} } elapsedTime = timer.getElapsedTimeInMilliSec(); cout<<"eigen time : "<<elapsedTime<<" ms"<<endl; // OpenCV cv::Mat ocvA(4, 1, CV_64F); cv::Mat ocvB(4, 1, CV_64F); timer.start(); for (i = 0; i < bnd; ++i) { res = cv::sum(cv::abs(ocvA - ocvB))[0]; //res = 0; //for (int idx = 0; idx < 4; ++idx) //{ // res += abs(ocvA.at<double>(idx, 0) - ocvB.at<double>(idx, 0)); //} } elapsedTime = timer.getElapsedTimeInMilliSec(); cout<<"opencv time : "<<elapsedTime<<" ms"<<endl; // pointer operation double *a = (double*)_mm_malloc(4 * sizeof(double), 16); double *b = (double*)_mm_malloc(4 * sizeof(double), 16); int len = ocvA.rows; timer.start(); for (i = 0; i < bnd; ++i) { res = 0; for (int idx = 0; idx < len; ++idx) { res += abs(a[idx] - b[idx]); } //cout<<"i = "<<i<<endl; } elapsedTime = timer.getElapsedTimeInMilliSec(); cout<<"array operation : "<<elapsedTime<<" ms"<<endl; // release resource _mm_free(a); _mm_free(b); return 0; } 其中的計時函數用到的是 Franz Kafka 提供的跨平台高精度計時類,可以從以下地址下載 High Resolution Timer。 用以上代碼在 release 下得到的結果如下: [plain] arma time : 0.87827 ms eigen time : 0.13641 ms opencv time : 179.599 ms array operation : 0.135591 ms 可以看出 Eigen 的時間和直接用數組運算的時間是相當的,Armadillo 的時間慢了 6~7 倍左右,而 OpenCV 已經目不忍視了,不知道 OpenCV 是怎麼想的,差距有點懸殊。 下面又做了另外一組對比,把循環中的求 SAD 部分用類似於數組的方式自己計算,結果如下 [plain] arma time : 0.145423 ms eigen time : 0.134772 ms opencv time : 0.134362 ms array operation : 0.139278 ms 這下計算時間基本上是相當的了。 通過這些對比得到兩個結論: 1、雖然這些庫在矩陣相乘等操作上可能比較高效,但是對於某些低級操作可能效率並不高 2、通過模板訪問數據並不比數組效率低,性能基本相當