2016-07-24 34 views
2

我试图计算一个数组的分量指数的总和。阵列是一个Eigen::ArrayXd,所以我期望Eigen的exp函数将被优化为相同的代码,或更好,比手动循环。相反,我看到一个手动循环快了几十个百分点。 (在下面的例子中,Eigen大约为2.1秒,手动循环为1.6秒。)特征表达模板比指数的手动循环慢

我没有使用任何特征向量化(SSE被禁用),MKL或其他任何特殊的东西。这只是一个默认的Visual Studio 2010项目,在Release配置上,带有Eigen 3.2.9开箱即用。指定“完全优化”(/ Ox)和“优先快速代码”(/ Ot)对结果没有影响。

我没有足够的知识来查看编译代码以了解发生了什么 - 任何人都可以提出为什么直接使用Eigen可能会更慢,以及如何哄它产生与手动循环相同的性能?

#include <ctime> 
#include <iostream> 
#include <Eigen/Dense> 

int main(int argc, char* argv[]) 
{ 
    const Eigen::ArrayXd xs(Eigen::ArrayXd::Random(1000000)); 
    Eigen::ArrayXd array_result(Eigen::ArrayXd::Zero(xs.size())), loop_result(Eigen::ArrayXd::Zero(xs.size())); 

    { 
     std::clock_t start_time = std::clock(); 
     for (int i = 1; i <= 100; ++i) { 
      const double b = i; // Values not important; 
      array_result += exp(b * xs); 
     } 
     std::clock_t end_time = std::clock(); 
     std::cout << "Array time (seconds): " << static_cast<double>(end_time - start_time)/CLOCKS_PER_SEC << std::endl; 
    } 

    { 
     std::clock_t start_time = std::clock(); 
     for (int i = 1; i <= 100; ++i) { 
      const double b = i; // Values not important; 
      for (Eigen::ArrayXd::Index i = 0; i < xs.size(); ++i) { 
       loop_result[i] += exp(b * xs[i]); 
      } 
     } 
     std::clock_t end_time = std::clock(); 
     std::cout << "Loop time (seconds): " << static_cast<double>(end_time - start_time)/CLOCKS_PER_SEC << std::endl; 
    } 

    system("pause"); 
    return 0; 
} 
+0

你试过'(b * xs).exp()'吗?你定义了'EIGEN_DONT_VECTORIZE'的 –

+0

? – kangshiyin

+0

@Avi,我尝试过'(b * xs).exp()' - 表现看起来相同。 @ kangshiyin,定义'EIGEN_DONT_VECTORIZE'似乎不会影响性能。 – GavinTruter

回答

2

如果定义EIGEN_DONT_VECTORIZE,两种方法都将在C++代码方面几乎相同,循环在阵列的每个元素。

随着/Ox/Ot,你告诉你的编译器自动矢量化循环。但是由Eigen生成的循环可能比手写循环稍复杂/低效。因此表现较低。

这也取决于编译器。使用不自动矢量化循环的编译器时,Eigen与EIGEN_DONT_VECTORIZE几乎与循环方法相同。

$ g++ -DEIGEN_DONT_VECTORIZE -g -O3 -DNDEBUG eigen-speed.cpp -o eigen-speed && ./eigen-speed 
Array time (seconds): 1.94 
Loop time (seconds): 1.93 

启用特征向量化后,Eigen表现更好。

$ g++ -g -O3 -DNDEBUG eigen-speed.cpp -o eigen-speed && ./eigen-speed 
Array time (seconds): 0.63 
Loop time (seconds): 1.86 

随着编译器自动向量化的循环,与本征是EIGEN_DONT_VECTORIZE仍然类似于循环的办法,但都被编译器量化。

$ icpc -DEIGEN_DONT_VECTORIZE -g -O3 -DNDEBUG eigen-speed.cpp -o eigen-speed && ./eigen-speed 
Array time (seconds): 0.43 
Loop time (seconds): 0.38 

但是,当您启用征矢量,

$ icpc -g -O3 -DNDEBUG eigen-speed.cpp -o eigen-speed && ./eigen-speed 
Array time (seconds): 0.7 
Loop time (seconds): 0.36 

$ icpc -mavx -g -O3 -DNDEBUG eigen-speed.cpp -o eigen-speed && ./eigen-speed 
Array time (seconds): 0.32 
Loop time (seconds): 0.18 

表明本征的矢量EXP()的代码比编译器的自动向量化版本差,既为Eigen's SSE versionEigen's AVX version

所以你可以看到VS更像icpc

+0

有趣的是,矢量化可能会恶化性能,谢谢。你说Eigen产生的循环可能效率低于手写循环(就像你在第三组结果和我的结果中看到的那样),你能解释一下为什么吗?据我了解,表达式模板应该编译成与循环完全相同的代码。 – GavinTruter

+0

@GavinTruter这是一个猜测。我没有检查特征码。也许它在主循环之前和之后有一个头部和一个尾部,以处理未对齐的数据,最初是为了矢量化代码。 – kangshiyin