C++ 为什么valarray这么慢?
我尝试使用valarray,因为它在操作向量和矩阵时非常类似于MATLAB。我首先做了一些性能检查,发现valarray无法达到Stroustrup在书中声明的性能 这个测试程序实际上做了500万次双倍乘法。我认为c=a*b至少可以与循环双类型元素乘法的C++ 为什么valarray这么慢?,c++,valarray,C++,Valarray,我尝试使用valarray,因为它在操作向量和矩阵时非常类似于MATLAB。我首先做了一些性能检查,发现valarray无法达到Stroustrup在书中声明的性能 这个测试程序实际上做了500万次双倍乘法。我认为c=a*b至少可以与循环双类型元素乘法的相提并论,但我完全错了。我尝试了几台计算机和微软Visual C++ 6和VisualStudio2008。 顺便说一下,我使用以下代码在MATLAB上进行了测试: len = 5*1024*1024; a = rand(len, 1); b =
相提并论,但我完全错了。我尝试了几台计算机和微软Visual C++ 6和VisualStudio2008。
顺便说一下,我使用以下代码在MATLAB上进行了测试:
len = 5*1024*1024;
a = rand(len, 1);
b = rand(len, 1);
c = zeros(len, 1);
tic;
c = a.*b;
toc;
结果为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;
}
具有相同优化的调试模式:
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
------------------------------------------------------
我怀疑原因是c=a*b
比一次执行一个元素的操作慢得多,原因是
template<class T> valarray<T> operator*
(const valarray<T>&, const valarray<T>&);
模板数组运算符*
(const-valarray&,const-valarray&);
运算符必须分配内存将结果放入,然后按值返回
即使使用“swapoptimization”执行复制,该函数的开销仍然是
- 为生成的
valarray
- 初始化新的
valarray
(这可能会被优化掉)
- 将结果放入新的
valarray
- 初始化或使用结果值设置新的
valarray
时在内存中分页
- 取消分配由结果替换的旧
valarray
我刚刚在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
英特尔ICC 12.0.2:
double operator* 7.76757 ms
valarray operator* 9.60208 ms
valarray[i] operator* 7.51409 ms
在这两种情况下,我只使用了-O3
,没有其他与优化相关的标志
看起来像是MS+C++编译器和/或ValStudio实现吸吮。
以下是针对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, ×tamp);
return timestamp.tv_sec * 1000.0 + timestamp.tv_nsec * 1.0e-6;
}
#包括
#包括
#包括
#包括
使用名称空间std;
双gettime_hp();
int main()
{
枚举{N=5*1024*1024};
阵列a(N),b(N),c(N);
int i,j;
对于(j=0;j,我在VisualStudio2010的x64版中编译。我对您的代码做了很小的更改:
double* a1 = &a[0], *b1 = &b[0], *c1 = &c[0];
double dtime = gettime_hp();
for (i=0 ; i<N ; ++i)
a1[i] *= b1[i];
dtime = gettime_hp() - dtime;
cout << "double operator* " << dtime << " ms\n";
dtime = gettime_hp();
a *= b;
dtime = gettime_hp() - dtime;
cout << "valarray operator* " << dtime << " ms\n";
dtime = gettime_hp();
for (i=0 ; i<N ; ++i)
a[i] *= b[i];
dtime = gettime_hp() - dtime;
cout << "valarray[i] operator* " << dtime<< " ms\n";
cout << "------------------------------------------------------\n" ;
double*a1=&a[0]、*b1=&b[0]、*c1=&c[0];
double dtime=gettime_hp();
对于(i=0;i<p>),我终于通过延迟评估得到了这一点。因为我刚开始学习C++高级概念,代码可能很难看。
代码如下:
#include <iostream>
#include <valarray>
#include <iostream>
#include "windows.h"
using namespace std;
SYSTEMTIME stime;
LARGE_INTEGER sys_freq;
double gettime_hp();
// To improve the c = a*b (it will generate a temporary first, assigned to 'c' and delete the temporary.
// Which causes the program really slow
// The solution is the expression template and let the compiler to decide when all the expression is known.
// Delayed evaluation
//typedef valarray<double> Vector;
class Vector;
class VecMul
{
public:
const Vector& va;
const Vector& vb;
//Vector& vc;
VecMul(const Vector& v1, const Vector& v2): va(v1), vb(v2) {}
operator Vector();
};
class Vector:public valarray<double>
{
valarray<double> *p;
public:
explicit Vector(int n)
{
p = new valarray<double>(n);
}
Vector& operator = (const VecMul &m)
{
for(int i=0; i<m.va.size(); i++)
(*p)[i] = (m.va)[i]*(m.vb)[i]; // Ambiguous
return *this;
}
double& operator[](int i) const {return (*p)[i];} //const vector_type[i]
int size()const {return (*p).size();}
};
inline VecMul operator*(const Vector& v1, const Vector& v2)
{
return VecMul(v1, v2);
}
int main()
{
enum {N = 5*1024*1024};
Vector 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;
}
valarray的全部要点是在向量机上实现快速,而x86机器则不然
在非Vector机器上的一个好的实现应该能够与您通过以下方式获得的性能相匹配
for (i=0; i < N; ++i)
c1[i] = a1[i] * b1[i];
(i=0;i
c1[i]=a1[i]*b1[i];
坏的当然不会。除非硬件中有加快并行处理的东西,否则这将非常接近您所能做到的最佳状态。嗯……我测试过,它与valarray相同……而且,Blitz++[]
操作符非常慢
#包括
#包括
#ifdef WIN32
#包括“windows.h”
大整数系统频率;
#恩迪夫
#ifdef LINUX
#恩迪夫
使用名称空间std;
系统时间;
__forceinline双gettime_hp();
双gettime_hp()
{
#ifdef WIN32
大整数滴答声;
外部大整数系统频率;
QueryPerformanceCounter(&勾选);
返回(双)勾选.QuadPart*1000.0/sys\u freq.QuadPart;
#恩迪夫
#ifdef LINUX
结构timespec时间戳;
时钟获取时间(时钟实时和时间戳);
返回timestamp.tv_sec*1000.0+timestamp.tv_nsec*1.0e-6;
#恩迪夫
}
BZ_使用_名称空间(blitz)
int main()
{
int N=5*1024*1024;
//创建双精度三维阵列
阵列a(N),b(N),c(N);
int i,j;
#ifdef WIN32
QueryPerformanceFrequency(&sys\u freq);
#恩迪夫
对于(j=0;j我认为Michael Burr的回答是正确的。也许您可以创建一个虚拟类型作为操作符+
返回值的类型,并为这个虚拟类型重新加载另一个操作符=
,比如操作符=(虚拟类型&v){&valarray=&v;v=NULL;}
(粗略地说)
当然,在valarray上实现这个想法是很困难的。但是当您创建一个新类时,您可以尝试这个想法。然后,operator+
的效率几乎与operator+=相同。您是否运行了可执行文件?或者您是否在调试器(通过visual studio)中尝试过它?您使用的优化设置是什么?调试器和exeVC6都没有区别?真的吗?它已经有13年的历史了,早于标准。在GCC 4.6.1上,使用-flto-O3-march=native-std=c++0x
,我在所有三种情况下都获得了几乎相同的性能,从第一种到第三种略有增加。我刚刚搜索过,它实际上重新运行了ns a reference:template inline valarray&operator*=(valarray&_L,const&_R){u VALGOP2(*=_R);}您在上面的注释中发布的声明与问题中发布的代码中使用的声明有两个不同:1)operator*=
不同于使用operator*()
后跟operator=()
,和2)这是*=
运算符的声明,该运算符使用标量参数乘以valarray
,根据分析,如果需要性能,我们无法使用valarray。但是,根据书中所述,该类是专为提高性能而设计的。你能给我一些分数吗?有吗其他方法允许我把数组作为一个整体来处理,就像C++中的ValSub一样?Thanks@Michael:您可能是对的,但在另一个答案中可以看到我的Linux benchamrks-如果您使用一个像样的编译器和valarray实现,valarray的性能显然不需要明显低于直接循环。新闻快讯:va允许使用larray算法,但不需要
double operator* 41.2031 ms
valarray operator* 43.8407 ms
valarray[i] operator* 42.49 ms
for (i=0; i < N; ++i)
c1[i] = a1[i] * b1[i];