问题描述
|
显然,在分析(科学计算)C ++代码之后,有25%(!)的时间花费在调用calls0ѭ上。没错,我的代码将所有时间都花在ѭ1s(还有一些
vector<int>
s)上,但我仍然想知道,与C风格相比,是否应该有significant3 significant的大量开销数组?
(我已经看到了另一个有关SO的问题,但是关于[]
vsat()
,但是显然even4ѭ对我来说太慢了?!)
谢谢,
安东尼
(编辑:仅供参考:在Ubuntu上使用g ++ -O3 4.5.2版)
解决方法
std::vector::operator[]
应该相当高效,但是编译器必须是偏执狂,对于函数的每次调用,都必须假定向量可能已移至内存中的其他位置。
例如此代码
for (int i=0,n=v.size(); i<n; i++)
{
total += v[i] + foo();
}
如果事先不知道foo
的代码,则每次编译器都被迫重新加载向量起始地址,因为向量可能由于foo()
中的代码而被重新分配。
如果您确定该向量不会在内存中移动或重新分配,则可以使用以下方法将查找操作排除在外:
double *vptr = &v[0]; // Address of first element
for (int i=0,n=v.size(); i<n; i++)
{
total += vptr[i] + foo();
}
采用这种方法,可以节省一次存储器查找操作(整个循环可能会在寄存器中存入vptr
)。
效率低下的另一个原因可能是缓存破坏。要查看这是否是一个问题,一个简单的技巧就是通过一些数量不均匀的元素来过度分配向量。
原因是因为如果您有很多向量,例如所有4096个元素的地址中都将具有相同的低位,并且由于高速缓存行无效,您可能最终会失去很多速度。
例如在我的电脑上的这个循环
std::vector<double> v1(n),v2(n),v3(n),v4(n),v5(n);
for (int i=0; i<1000000; i++)
for (int j=0; j<1000; j++)
{
v1[j] = v2[j] + v3[j];
v2[j] = v3[j] + v4[j];
v3[j] = v4[j] + v5[j];
v4[j] = v5[j] + v1[j];
v5[j] = v1[j] + v2[j];
}
如果n == 8191
,则执行约8.1秒;如果n == 10000
,则执行3.2秒。注意,内部循环始终从0到999,与n
的值无关;不同的只是内存地址。
根据处理器/体系结构的不同,我已经观察到由于缓存破坏而导致的速度甚至降低了10倍。
,在现代的编译器中,在释放模式下且启用了优化,与原始指针相比,使用operator []
不会产生开销:调用完全内联,并且解析为仅指针访问。
我猜想您是以某种方式复制了赋值中的返回值,这实际上导致了在指令中花费了25%的时间。[与float
和int
不相关]
或者,您的其余代码简直是飞快的。
,是的,会有一些开销,因为通常“ѭ20”将包含指向动态分配的数组的指针,其中数组只是\“ there \”。这意味着在数组上使用[]
时,通常在vector::operator[]
中会有额外的内存取消引用。 (请注意,如果您有一个指向数组的指针,通常不如vector
好。)
如果您正在同一代码段中通过相同的vector
或指针执行多次访问而又不导致向量可能需要重新分配,则这种额外取消引用的开销可能会在多次访问中分担,并且可以忽略不计。
例如。
#include <vector>
extern std::vector<float> vf;
extern float af[];
extern float* pf;
float test1(long index)
{
return vf[index];
}
float test2(long index)
{
return af[index];
}
float test3(long index)
{
return pf[index];
}
在g ++上为我生成以下代码(修剪了一些guff):
.globl _Z5test1i
.type _Z5test1i,@function
_Z5test1i:
movq vf(%rip),%rax
movss (%rax,%rdi,4),%xmm0
ret
.size _Z5test1i,.-_Z5test1i
.globl _Z5test2i
.type _Z5test2i,@function
_Z5test2i:
movss af(,%xmm0
ret
.size _Z5test2i,.-_Z5test2i
.globl _Z5test3i
.type _Z5test3i,@function
_Z5test3i:
movq pf(%rip),%xmm0
ret
.size _Z5test3i,.-_Z5test3i
请注意,指针和向量版本如何产生完全相同的代码,而仅使用数组版本“ winning \”。
,通常,应该没有显着差异。差异可以
实际上,由于各种原因,这取决于
编译器优化特定的代码位。一个重大的可能
区别:您正在分析,这意味着您正在执行
检测代码。我不知道您使用的是什么探查器,但是
编译器经常出于各种原因关闭内联
进行性能分析的工具。您确定不是这种情况吗
在这里,这是人为地导致索引出现
比内联要花费更多的时间。
,纯数组访问是(几乎)直接内存读取,而operator []是vector <>的成员方法。
如果内联正确,则应该相同,否则,对于计算密集型工作,开销非常大。