C++ 基本C/C++;解释

C++ 基本C/C++;解释,c++,c,C++,C,我想检查计算机视觉中一个算法的性能,最后我用这段基本代码来检查哪个循环最快。但我对结果没有任何解释。我通常得到的结果表明,双for循环比简单循环快3倍。如果我切换两个循环,我会得到相同的结果,这意味着第二个循环总是优化的。。。那么编译器会进行什么样的优化呢 对不起,我知道这一定是个愚蠢的问题 ulong k = 0; auto start = std::chrono::high_resolution_clock::now(); for( uint i = 0; i < 1000000; +

我想检查计算机视觉中一个算法的性能,最后我用这段基本代码来检查哪个循环最快。但我对结果没有任何解释。我通常得到的结果表明,双for循环比简单循环快3倍。如果我切换两个循环,我会得到相同的结果,这意味着第二个循环总是优化的。。。那么编译器会进行什么样的优化呢

对不起,我知道这一定是个愚蠢的问题

ulong k = 0;
auto start = std::chrono::high_resolution_clock::now();
for( uint i = 0; i < 1000000; ++i )
{
    k++;
}
auto diff = std::chrono::high_resolution_clock::now() - start;
auto t1 = std::chrono::duration_cast<std::chrono::nanoseconds>(diff);

k = 0;
start = std::chrono::high_resolution_clock::now();
for( uint i = 0; i < 1000; ++i )
{
    for( uint j = 0; j < 1000; ++j )
    {
        k++;
    }
}
diff = std::chrono::high_resolution_clock::now() - start;
auto t2 = std::chrono::duration_cast<std::chrono::nanoseconds>(diff);

CL_PRINT( "Simple: ", t1.count() );
CL_PRINT( "Double: ", t2.count() );
ulong k=0;
自动启动=标准::时钟::高分辨率时钟::现在();
对于(uint i=0;i<1000000;++i)
{
k++;
}
auto diff=std::chrono::高分辨率时钟::now()-开始;
自动t1=std::chrono::持续时间(差异);
k=0;
开始=标准::时钟::高分辨率时钟::现在();
对于(uint i=0;i<1000;++i)
{
对于(uint j=0;j<1000;++j)
{
k++;
}
}
diff=std::chrono::高分辨率时钟::now()-开始;
自动t2=std::chrono::持续时间(差异);
CL_打印(“简单:,t1.count());
CL_打印(“双精度:,t2.count());
如果我切换这两个循环,我会得到相同的结果,这意味着第二个循环总是优化的

请注意,
CL\u PRINT
只是一个用于调试的宏。
还请注意,我使用以下选项编译代码:
-O3-msse4.1

这里的答案是确切的时间会有所不同。在我的机器上运行时,第一个循环有时会出现1000,第二个循环有时会出现1000。计时器滴答作响,这只是“运气”。如果您有一个更精确的计时器,它可能会根据读取计时器所需的时间或类似的时间显示差异

$ ./a.out
k = 1000000
k = 1000000
Simple: 0
Double: 1000
$ ./a.out
k = 1000000
k = 1000000
Simple: 1000
Double: 0
$ ./a.out
k = 1000000
k = 1000000
Simple: 1000
Double: 0
$ ./a.out
k = 1000000
k = 1000000
Simple: 1000
Double: 0
很容易看出,这两个循环都经过了优化:

main:
.LFB1474:
.cfi_startproc
pushq   %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
pushq   %rbx
.cfi_def_cfa_offset 24
.cfi_offset 3, -24
subq    $8, %rsp
.cfi_def_cfa_offset 32
call    _ZNSt6chrono12system_clock3nowEv
movq    %rax, %rbx
call    _ZNSt6chrono12system_clock3nowEv
movl    $.LC0, %esi
**subq  %rbx, %rax**
movl    $_ZSt4cout, %edi
imulq   $1000, %rax, %rbp
call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
**movl  $1000000, %esi**
movq    %rax, %rdi
call    _ZNSo9_M_insertImEERSoT_
movq    %rax, %rdi
call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
call    _ZNSt6chrono12system_clock3nowEv
movq    %rax, %rbx
call    _ZNSt6chrono12system_clock3nowEv
movl    $.LC0, %esi
**subq  %rbx, %rax**
movl    $_ZSt4cout, %edi
imulq   $1000, %rax, %rbx
call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
**movl  $1000000, %esi**
movq    %rax, %rdi
call    _ZNSo9_M_insertImEERSoT_
movq    %rax, %rdi
call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
movl    $.LC1, %esi
movl    $_ZSt4cout, %edi
call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movq    %rbp, %rsi
movq    %rax, %rdi
call    _ZNSo9_M_insertIlEERSoT_
movq    %rax, %rdi
call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
movl    $.LC2, %esi
movl    $_ZSt4cout, %edi
call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movq    %rbx, %rsi
movq    %rax, %rdi
call    _ZNSo9_M_insertIlEERSoT_
movq    %rax, %rdi
call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
addq    $8, %rsp
.cfi_def_cfa_offset 24
xorl    %eax, %eax
popq    %rbx
.cfi_def_cfa_offset 16
popq    %rbp
.cfi_def_cfa_offset 8
ret

您可以清楚地看到K的常数作为常数插入到流中,并且取“before”和“after”的时间,然后减去,中间没有(很多)代码。(有趣的位用
**…**
-当然代码中没有加粗)

您是否阅读了生成的代码,以查看所有循环是否完好无损地“幸存”?在
-O3
中,如果编译器注意到
k
的值从未被使用过,并且对代码进行了显著的重新构造,我不会感到惊讶。lol“re structured…Implemental”是“消除死代码”的一个令人敬畏的委婉说法。不,我没有,但我会的。我不知道编译器可以优化那么多。。。所以编译器确实优化了它,但是为什么第二个循环总是更快呢?它是同一个“死代码”,所以在这两种情况下都应该对它进行优化,不是吗?是的,这就是为什么它实际上被允许为此代码打印一些内容:for(;);printf(“你好”);-永久循环没有副作用,因此编译器可以对其重新排序或完全删除它。试着让你的k不稳定,所以这是不允许的。这是一个合理的问题,我没有得到反对票。允许编译器优化代码的唯一原因是,如果编译器能够证明结果不会有任何“可见效果”(经过的时间不计算),并且两个循环在该标准方面是相同的。所以我怀疑答案是不合理的:你已经碰到了一个优化启发式行为愚蠢的地方。我怀疑编译器尤其没有“意识到”
k=0跟随第一个循环意味着不需要此计算。