犰狳inplace_plus明显慢于"normal"加操作



我正在用Armadillo 4.500.0编写一个程序,我发现像s += v * v.t() * q;这样的就地计算明显慢于等效的s = s + v * v.t() * q;,其中svq是适当大小的矢量。

当我运行以下代码时,发现原位版本比其他版本慢了好几倍,对于500个元素,速度慢了480倍(5.13秒到0.011秒),并进行了积极的优化(-O3或-Ofast;Apple LLVM 6.0版(clang-600.0.54))

#include <iostream>
#include <armadillo>
#include <sys/time.h>
using namespace arma;
using namespace std;
#define N_ELEM 500
#define REP 10000
int main(int argc, const char * argv[]) {
    timeval start;
    timeval end;
    double tInplace, tNormal;
    vec s = randu<vec>(N_ELEM);
    vec v = randu<vec>(N_ELEM);
    vec q = randu<vec>(N_ELEM);
    gettimeofday(&start, NULL);
    for(int i = 0; i < REP; ++i) {
        s += v * v.t() * q;
    }
    gettimeofday(&end, NULL);
    tInplace = (end.tv_sec - start.tv_sec + ((end.tv_usec - start.tv_usec) / 1e6));
    gettimeofday(&start, NULL);
    for(int i = 0; i < REP; ++i) {
        s = s + v * v.t() * q;
    }
    gettimeofday(&end, NULL);
    tNormal = (end.tv_sec - start.tv_sec + ((end.tv_usec - start.tv_usec) / 1e6));
    cout << "Inplace: " << tInplace << "; Normal: " << tNormal << " --> " << "Normal is " << tInplace / tNormal << " times faster" << endl;
    return 0;
}

有人能解释为什么就地操作符的性能差得多吗?尽管它可以使用已经可用的内存,所以不需要复制任何东西?

v.t() * q周围加括号将解决问题:

for(int i = 0; i < REP; ++i) {
    s += v * (v.t() * q);
}

使用括号强制执行评估顺序。表达式(v.t() * q)将计算为标量(技术上为1x1矩阵),然后将其用于乘以v向量。括号还将防止v * v.t()变成显式外积。

Armadillo在使用s = s + v * v.t() * q表达式时可以自动计算出这一点,但(目前)在使用原位运算符+=时需要更多提示。

最新更新