vector :: operator []开销

时间:2011-05-24 12:26:20

标签: c++ stl vector

显然,在对我的(科学计算)C ++代码进行概要分析后,调用vector::operator[]花费了25%(!)的时间。是的,我的代码花了所有的时间在vector<float> s(以及一些vector<int> s)进行读写,但是,我仍然想知道是否应该有一些重要的开销与{C风格的数组相比,operator[]

(我在SO上看到了另一个相关问题,但关于[] vs at() - 但显然甚至[]对我来说太慢了?!)

谢谢, 安东尼

(编辑:仅供参考:在Ubuntu上使用g ++ -O3版本4.5.2)

5 个答案:

答案 0 :(得分:12)

在现代编译器中,在发布模式下,启用优化后,与原始指针相比,使用operator []时出现开销:调用完全内联并解析为指针访问。

我猜你在某种程度上复制了赋值中的返回值,并且 this 导致在指令中花费了25%的实际时间。 [不相关适用于floatint]

或者你的其余代码非常快。

答案 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;的成员方法。

如果内联正确,它应该是相同的,否则,开销对于计算密集型工作非常重要。