显然,在对我的(科学计算)C ++代码进行概要分析后,调用vector::operator[]
花费了25%(!)的时间。是的,我的代码花了所有的时间在vector<float>
s(以及一些vector<int>
s)进行读写,但是,我仍然想知道是否应该有一些重要的开销与{C风格的数组相比,operator[]
?
(我在SO上看到了另一个相关问题,但关于[]
vs at()
- 但显然甚至[]
对我来说太慢了?!)
谢谢, 安东尼
(编辑:仅供参考:在Ubuntu上使用g ++ -O3版本4.5.2)
答案 0 :(得分:12)
在现代编译器中,在发布模式下,启用优化后,与原始指针相比,使用operator []
时出现无开销:调用完全内联并解析为指针访问。
我猜你在某种程度上复制了赋值中的返回值,并且 this 导致在指令中花费了25%的实际时间。 [不相关适用于float
和int
]
或者你的其余代码非常快。
答案 1 :(得分:9)
是的,会有一些开销,因为通常vector
将包含指向动态分配的数组的指针,其中数组就是“那里”。这意味着vector::operator[]
在数组上使用[]
时通常会有额外的内存解除引用。 (注意,如果你有一个指针到一个数组,这通常不比vector
好。)
如果您通过相同代码段中的相同vector
或指针执行多次访问而不会导致向量可能需要重新分配,则可以通过多次访问共享此额外取消引用的成本,并且可能可以忽略不计。
E.g。
#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 trimmed):
.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(,%rdi,4), %xmm0
ret
.size _Z5test2i, .-_Z5test2i
.globl _Z5test3i
.type _Z5test3i, @function
_Z5test3i:
movq pf(%rip), %rax
movss (%rax,%rdi,4), %xmm0
ret
.size _Z5test3i, .-_Z5test3i
注意指针和矢量版本如何生成完全相同的代码,只有数组版本“获胜”。
答案 2 :(得分:8)
一般来说,应该没有明显的区别。差异可以 然而,在实践中出于各种原因,取决于如何 编译器优化特定的代码位。一个重要的可能 差异:你正在分析,这意味着你正在执行 检测代码。我不知道你在使用什么样的探查器,但它确实如此 由于各种原因,编译器频繁关闭内联 用于分析。你确定不是这样吗? 在这里,这是人为地导致索引出现 比内联时间更长的时间。
答案 3 :(得分:6)
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倍。
答案 4 :(得分:1)
纯数组访问是(几乎)直接的内存读取,而operator []是vector&lt;&gt;的成员方法。
如果内联正确,它应该是相同的,否则,开销对于计算密集型工作非常重要。