为什么valarray这么慢?

sha*_*ing 20 c++ valarray

请原谅我对valarray的问题.我正在尝试使用它,因为它在操作矢量和矩阵时非常类似于matlab.我首先做了一些性能检查,发现valarray无法实现stroustrup在c ++编程语言中声明的性能.

测试程序实际上做了双倍的5M乘法.我认为c = a*b至少可以与for循环双重型元素乘法相媲美,但我完全错了.试过几台电脑和vc6.0和vs2008.

顺便说一句,我使用以下代码在matlab上测试:

len = 5*1024*1024;
a = rand(len, 1);
b = rand(len, 1);
c = zeros(len, 1);
tic;
c = a.*b;
toc;
Run Code Online (Sandbox Code Playgroud)

结果是46ms.这个时间精度不高,仅作为参考.

代码是:

#include <iostream>
#include <valarray>
#include <iostream>
#include "windows.h"

using namespace std;
SYSTEMTIME stime;
LARGE_INTEGER sys_freq;

double gettime_hp();

int main()
{
    enum { N = 5*1024*1024 };
    valarray<double> a(N), b(N), c(N);
    QueryPerformanceFrequency(&sys_freq);
    int i, j;
    for (j=0 ; j<8 ; ++j)
    {
        for (i=0 ; i<N ; ++i)
        {
            a[i] = rand();
            b[i] = rand();
        }

        double* a1 = &a[0], *b1 = &b[0], *c1 = &c[0];
        double dtime = gettime_hp();
        for (i=0 ; i<N ; ++i)
            c1[i] = a1[i] * b1[i];
        dtime = gettime_hp()-dtime;
        cout << "double operator* " << dtime << " ms\n";

        dtime = gettime_hp();
        c = a*b ;
        dtime = gettime_hp() - dtime;
        cout << "valarray operator* " << dtime << " ms\n";

        dtime = gettime_hp();
        for (i=0 ; i<N ; ++i)
            c[i] = a[i] * b[i];
        dtime = gettime_hp() - dtime;
        cout << "valarray[i] operator* " << dtime<< " ms\n";

        cout << "------------------------------------------------------\n";
    }
}

double gettime_hp()
{
    LARGE_INTEGER tick;
    extern LARGE_INTEGER sys_freq;
    QueryPerformanceCounter(&tick);
    return (double)tick.QuadPart * 1000.0 / sys_freq.QuadPart;
}
Run Code Online (Sandbox Code Playgroud)

运行结果:(具有最大速度优化的释放模式)

double operator* 52.3019 ms
valarray operator* 128.338 ms
valarray[i] operator* 43.1801 ms
------------------------------------------------------
double operator* 43.4036 ms
valarray operator* 145.533 ms
valarray[i] operator* 44.9121 ms
------------------------------------------------------
double operator* 43.2619 ms
valarray operator* 158.681 ms
valarray[i] operator* 43.4871 ms
------------------------------------------------------
double operator* 42.7317 ms
valarray operator* 173.164 ms
valarray[i] operator* 80.1004 ms
------------------------------------------------------
double operator* 43.2236 ms
valarray operator* 158.004 ms
valarray[i] operator* 44.3813 ms
------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

具有相同优化的调试模式:

double operator* 41.8123 ms
valarray operator* 201.484 ms
valarray[i] operator* 41.5452 ms
------------------------------------------------------
double operator* 40.2238 ms
valarray operator* 215.351 ms
valarray[i] operator* 40.2076 ms
------------------------------------------------------
double operator* 40.5859 ms
valarray operator* 232.007 ms
valarray[i] operator* 40.8803 ms
------------------------------------------------------
double operator* 40.9734 ms
valarray operator* 234.325 ms
valarray[i] operator* 40.9711 ms
------------------------------------------------------
double operator* 41.1977 ms
valarray operator* 234.409 ms
valarray[i] operator* 41.1429 ms
------------------------------------------------------
double operator* 39.7754 ms
valarray operator* 234.26 ms
valarray[i] operator* 39.6338 ms
------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

Pau*_*l R 23

我刚刚在Linux x86-64系统(Sandy Bridge CPU)上尝试过它:

gcc 4.5.0:

double operator* 9.64185 ms
valarray operator* 9.36987 ms
valarray[i] operator* 9.35815 ms
Run Code Online (Sandbox Code Playgroud)

英特尔ICC 12.0.2:

double operator* 7.76757 ms
valarray operator* 9.60208 ms
valarray[i] operator* 7.51409 ms
Run Code Online (Sandbox Code Playgroud)

在我刚刚使用的两种情况下-O3,没有其他与优化相关的标志.

看起来MS C++编译器和/或valarray实现很糟糕.


这是针对Linux修改的OP代码:

#include <iostream>
#include <valarray>
#include <iostream>
#include <ctime>

using namespace std ;

double gettime_hp();

int main()
{
    enum { N = 5*1024*1024 };
    valarray<double> a(N), b(N), c(N) ;
    int i,j;
    for(  j=0 ; j<8 ; ++j )
    {
        for(  i=0 ; i<N ; ++i )
        {
            a[i]=rand();
            b[i]=rand();
        }

        double* a1 = &a[0], *b1 = &b[0], *c1 = &c[0] ;
        double dtime=gettime_hp();
        for(  i=0 ; i<N ; ++i ) c1[i] = a1[i] * b1[i] ;
        dtime=gettime_hp()-dtime;
        cout << "double operator* " << dtime << " ms\n" ;

        dtime=gettime_hp();
        c = a*b ;
        dtime=gettime_hp()-dtime;
        cout << "valarray operator* " << dtime << " ms\n" ;

        dtime=gettime_hp();
        for(  i=0 ; i<N ; ++i ) c[i] = a[i] * b[i] ;
        dtime=gettime_hp()-dtime;
        cout << "valarray[i] operator* " << dtime<< " ms\n" ;

        cout << "------------------------------------------------------\n" ;
    }
}

double gettime_hp()
{
    struct timespec timestamp;

    clock_gettime(CLOCK_REALTIME, &timestamp);
    return timestamp.tv_sec * 1000.0 + timestamp.tv_nsec * 1.0e-6;
}
Run Code Online (Sandbox Code Playgroud)

  • +1.我在libc ++实现上运行了这个基准测试.它没有MS那么慢,但没有gcc那么快(它与你报告ICC的速度大致相同).事实证明我在表达式模板引擎中缺少一个键赋值运算符.补充一点.现在libc ++和gcc一样快.致OP:感谢速度测试!(问题上也是+1):-) (6认同)
  • 很好-仅供参考,您能添加构建中使用的选项吗(今晚我可能会玩这个东西...) (2认同)

Mic*_*urr 12

我怀疑原因c = a*b比执行操作慢得多一个元素一次就是

template<class T> valarray<T> operator*
    (const valarray<T>&, const valarray<T>&);
Run Code Online (Sandbox Code Playgroud)

运算符必须分配内存以将结果放入,然后按值返回.

即使使用"交换优化"来执行复制,该功能仍然具有开销

  • 为结果分配新块 valarray
  • 初始化新的valarray(这可能会被优化掉)
  • 把结果放到新的 valarray
  • valarray初始化或使用结果值设置时,在内存中为新内容进行分页
  • 解除分配valarray被结果取代的旧的

  • 新闻快讯:允许使用valarray算法但不要求使用*表达模板*(http://en.wikipedia.org/wiki/Expression_templates).使用表达式模板可以完全消除OP问题中的临时性,从而完全消除表达式"c = a*b"的堆分配和释放.很明显,gcc做到这一点(以及略微更正的libc ++),而MS C++则没有. (10认同)
  • 在上面的注释中发布的声明与在问题中发布的代码中使用的声明有两点不同:1)`operator*=`与使用`operator*()`后跟`operator =()不同`,和2)这是`*=`运算符的声明,它采用标量参数将`valarray`乘以 (2认同)

小智 5

valarray的全部要点是在矢量机上要快,而x86机却不行。

在非向量机上的良好实现应能够使您获得类似以下内容的性能

for (i=0; i < N; ++i) 
    c1[i] = a1[i] * b1[i];
Run Code Online (Sandbox Code Playgroud)

坏的当然不会。除非硬件中有某些东西可以加快并行处理的速度,否则它将接近您可以做的最好的事情。