为什么std :: vector :: operator []比std :: vector :: at()快5到10倍?

时间:2010-07-17 01:18:59

标签: c++ performance visual-c++ vector

在程序优化期间,尝试优化循环遍历向量的循环,我发现以下事实::: std :: vector :: at()比operator []快得多!

运营商[]比()快5到10倍,无论是发布还是发布。 debug builds(VS2008 x86)。

在网上读了一下让我意识到at()有边界检查。好的,但是,将操作放慢了10倍?!

有什么理由吗?我的意思是,边界检查是一个简单的数字比较,还是我错过了什么? 问题是这种性能受损的真正原因是什么? 更进一步,有没有办法让它更快

我肯定会在其他代码部分(我已经有自定义边界检查!)中将所有at()调用与[]交换。

概念证明:

#define _WIN32_WINNT 0x0400
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include <conio.h>

#include <vector>

#define ELEMENTS_IN_VECTOR  1000000

int main()
{
    __int64 freq, start, end, diff_Result;
    if(!::QueryPerformanceFrequency((LARGE_INTEGER*)&freq))
        throw "Not supported!";
    freq /= 1000000; // microseconds!

    ::std::vector<int> vec;
    vec.reserve(ELEMENTS_IN_VECTOR);
    for(int i = 0; i < ELEMENTS_IN_VECTOR; i++)
        vec.push_back(i);

    int xyz = 0;

    printf("Press any key to start!");
    _getch();
    printf(" Running speed test..\n");

    { // at()
        ::QueryPerformanceCounter((LARGE_INTEGER*)&start);
        for(int i = 0; i < ELEMENTS_IN_VECTOR; i++)
            xyz += vec.at(i);
        ::QueryPerformanceCounter((LARGE_INTEGER*)&end);
        diff_Result = (end - start) / freq;
    }
    printf("Result\t\t: %u\n\n", diff_Result);

    printf("Press any key to start!");
    _getch();
    printf(" Running speed test..\n");

    { // operator []
        ::QueryPerformanceCounter((LARGE_INTEGER*)&start);
        for(int i = 0; i < ELEMENTS_IN_VECTOR; i++)
            xyz -= vec[i];
        ::QueryPerformanceCounter((LARGE_INTEGER*)&end);
        diff_Result = (end - start) / freq;
    }

    printf("Result\t\t: %u\n", diff_Result);
    _getch();
    return xyz;
}

编辑:
现在该值被赋予“xyz”,因此编译器不会“擦除”它。

3 个答案:

答案 0 :(得分:57)

原因是可以使用单个处理器指令完成未经检查的访问。检查访问还必须从内存加载大小,将其与索引进行比较,并(假设它在范围内)跳过条件分支到错误处理程序。可能会有更多的事情来处理抛出异常的可能性。这将慢很多倍,这正是为什么你有两个选择。

如果您可以在没有运行时检查的情况下证明索引在范围内,那么请使用operator[]。否则,请使用at(),或在访问前添加自己的检查。 operator[]应尽可能快或多,但如果索引无效,则会混乱。

答案 1 :(得分:29)

我在我的机器上运行了测试代码:

在未经优化的调试版本中,两个循环之间的差异无关紧要。

在优化的发布版本中,第二个for循环完全被优化(对operator[]的调用很可能内联,优化器可以看到循环什么也不做,可以删除整个循环)。

如果我改变循环的主体来分别做一些实际的工作,例如vec.at(i)++;vec[i]++;,那么两个循环之间的差异是无关紧要的。

我看不到你看到的五到十倍的性能差异。

答案 2 :(得分:4)

您不对返回值执行任何操作,因此如果编译器内联这些函数,则可以完全优化它们。或者也许它可以完全优化掉下标([])版本。从性能测量的角度来看,没有优化的运行是没有用的,你需要的是一些简单但有用的程序来运用这些功能,这样它们就不会被优化掉。例如,您可以随机播放矢量(随机交换50000对元素)。