为什么在for循环中访问比在-O0中的范围内访问更快但在-O3中不访问?

Man*_*ero -1 c++ performance iterator c++11

我正在学习C++(和C++ 11)的性能.我需要在调试和发布模式下执行,因为我花时间进行调试和执行.

我对这两个测试感到惊讶,并且对不同的编译器标志优化有多大的改变.

测试迭代器1:

  • 优化0(-O0):更快.
  • 优化3(-O3):较慢.

测试迭代器2:

  • 优化0(-O0):较慢.
  • 优化3(-O3):更快.

PD:我使用以下时钟代码.

测试迭代器1:

void test_iterator_1()
{
    int z = 0;
    int nv = 1200000000;
    std::vector<int> v(nv);

    size_t count = v.size();

    for (unsigned int i = 0; i < count; ++i) {
        v[i] = 1;
    }
}
Run Code Online (Sandbox Code Playgroud)

测试迭代器2:

void test_iterator_2()
{
    int z = 0;
    int nv = 1200000000;
    std::vector<int> v(nv);

    for (int& i : v) {
        i = 1;
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:问题仍然是相同的,但对于-O3中的ranged-for,差异很小.所以对于循环1是最好的.

更新2:结果:

使用-O3:

t1: 80 units
t2: 74 units
Run Code Online (Sandbox Code Playgroud)

使用-O0:

t1: 287 units
t2: 538 units
Run Code Online (Sandbox Code Playgroud)

更新3:代码!.编译:g ++ -std = c ++ 11 test.cpp -O0(然后是-O3)

Zac*_*and 7

您的第一个测试实际上是将向量中每个元素的值设置为1.

您的第二个测试是将向量中每个元素的副本的值设置为1(原始向量是相同的).

当你进行优化时,第二个循环很可能被完全删除,因为它基本上什么都不做.

如果您希望第二个循环实际设置值:

for (int& i : v) // notice the & 
{
    i = 1;
}
Run Code Online (Sandbox Code Playgroud)

一旦进行了更改,您的循环可能会生成几乎完全相同的汇编代码.

作为旁注,如果您想将整个向量初始化为单个值,更好的方法是:

std::vector<int> v(SIZE, 1);
Run Code Online (Sandbox Code Playgroud)

编辑

组装相​​当长(100多行),所以我不会发布所有内容,但需要注意几点:

版本1将存储值count和增量i,每次都对其进行测试.版本2使用迭代器(基本相同std::for_each(b.begin(), v.end() ...)).因此,循环维护的代码是非常不同的(它是针对版本2的更多设置,但每次迭代的工作量更少).

版本1(只是循环的内容)

mov eax, DWORD PTR _i$2[ebp]
push    eax
lea ecx, DWORD PTR _v$[ebp]
call    ??A?$vector@HV?$allocator@H@std@@@std@@QAEAAHI@Z ; std::vector<int,std::allocator<int> >::operator[]
mov DWORD PTR [eax], 1
Run Code Online (Sandbox Code Playgroud)

版本2(只是循环的内容)

mov eax, DWORD PTR _i$2[ebp]
mov DWORD PTR [eax], 1
Run Code Online (Sandbox Code Playgroud)

当它们得到优化时,这一切都会发生变化(除了几条指令的排序),输出几乎相同.

版本1(优化)

    push    ebp
    mov ebp, esp
    sub esp, 12                 ; 0000000cH
    push    ecx
    lea ecx, DWORD PTR _v$[ebp]
    mov DWORD PTR _v$[ebp], 0
    mov DWORD PTR _v$[ebp+4], 0
    mov DWORD PTR _v$[ebp+8], 0
    call    ?resize@?$vector@HV?$allocator@H@std@@@std@@QAEXI@Z ; std::vector<int,std::allocator<int> >::resize
    mov ecx, DWORD PTR _v$[ebp+4]
    mov edx, DWORD PTR _v$[ebp]
    sub ecx, edx
    sar ecx, 2 ; this is the only differing instruction
    test    ecx, ecx
    je  SHORT $LN3@test_itera
    push    edi
    mov eax, 1
    mov edi, edx
    rep stosd
    pop edi
$LN3@test_itera:
    test    edx, edx
    je  SHORT $LN21@test_itera
    push    edx
    call    DWORD PTR __imp_??3@YAXPAX@Z
    add esp, 4
$LN21@test_itera:
    mov esp, ebp
    pop ebp
    ret 0
Run Code Online (Sandbox Code Playgroud)

版本2(优化)

    push    ebp
    mov ebp, esp
    sub esp, 12                 ; 0000000cH
    push    ecx
    lea ecx, DWORD PTR _v$[ebp]
    mov DWORD PTR _v$[ebp], 0
    mov DWORD PTR _v$[ebp+4], 0
    mov DWORD PTR _v$[ebp+8], 0
    call    ?resize@?$vector@HV?$allocator@H@std@@@std@@QAEXI@Z ; std::vector<int,std::allocator<int> >::resize
    mov edx, DWORD PTR _v$[ebp]
    mov ecx, DWORD PTR _v$[ebp+4]
    mov eax, edx
    cmp edx, ecx
    je  SHORT $LN1@test_itera
$LL33@test_itera:
    mov DWORD PTR [eax], 1
    add eax, 4
    cmp eax, ecx
    jne SHORT $LL33@test_itera
$LN1@test_itera:
    test    edx, edx
    je  SHORT $LN47@test_itera
    push    edx
    call    DWORD PTR __imp_??3@YAXPAX@Z
    add esp, 4
$LN47@test_itera:
    mov esp, ebp
    pop ebp
    ret 0
Run Code Online (Sandbox Code Playgroud)