c++ - 难以衡量 C/C++ 性能

标签 c++ c performance optimization measurement

我编写了一段 C 代码来说明关于优化和分支预测的讨论中的一个观点。然后我注意到比我预期的更多样化的结果。我的目标是用一种介于 C++ 和 C 之间的通用子集的语言编写它,这两种语言都符合标准并且相当可移植。它在不同的 Windows PC 上进行了测试:

#include <stdio.h>
#include <time.h>

/// @return - time difference between start and stop in milliseconds
int ms_elapsed( clock_t start, clock_t stop )
{
    return (int)( 1000.0 * ( stop - start ) / CLOCKS_PER_SEC );
}

int const Billion = 1000000000;
/// & with numbers up to Billion gives 0, 0, 2, 2 repeating pattern 
int const Pattern_0_0_2_2 = 0x40000002; 

/// @return - half of Billion  
int unpredictableIfs()
{
    int sum = 0;
    for ( int i = 0; i < Billion; ++i )
    {
        // true, true, false, false ...
        if ( ( i & Pattern_0_0_2_2 ) == 0 )
        {
            ++sum;
        }
    }
    return sum;
}

/// @return - half of Billion  
int noIfs()
{
    int sum = 0;
    for ( int i = 0; i < Billion; ++i )
    {
        // 1, 1, 0, 0 ...
        sum += ( i & Pattern_0_0_2_2 ) == 0;
    }
    return sum;
}

int main()
{
    clock_t volatile start;
    clock_t volatile stop;
    int volatile sum;
    printf( "Puzzling measurements:\n" );

    start = clock();
    sum = unpredictableIfs();
    stop = clock();
    printf( "Unpredictable ifs took %d msec; answer was %d\n"
          , ms_elapsed(start, stop), sum );

    start = clock();
    sum = unpredictableIfs();
    stop = clock();
    printf( "Unpredictable ifs took %d msec; answer was %d\n"
          , ms_elapsed(start, stop), sum );

    start = clock();
    sum = noIfs();
    stop = clock();
    printf( "Same without ifs took %d msec; answer was %d\n"
          , ms_elapsed(start, stop), sum );

    start = clock();
    sum = unpredictableIfs();
    stop = clock();
    printf( "Unpredictable ifs took %d msec; answer was %d\n"
          , ms_elapsed(start, stop), sum );
}

用VS2010编译;/O2 优化 Intel Core 2、WinXP 结果:

Puzzling measurements:
Unpredictable ifs took 1344 msec; answer was 500000000
Unpredictable ifs took 1016 msec; answer was 500000000
Same without ifs took 1031 msec; answer was 500000000
Unpredictable ifs took 4797 msec; answer was 500000000

编辑:编译器的完整切换:

/Zi /nologo /W3 /WX- /O2 /Oi /Oy- /GL /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /Gm- /EHsc /GS /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Fp"Release\Trying.pch" /Fa"Release\" /Fo"Release\" /Fd"Release\vc100.pdb" /Gd /analyze- /errorReport:queue

其他人发了这样的……用MinGW编译,g++ 4.71,-O1优化Intel Core 2,WinXP结果:

Puzzling measurements:
Unpredictable ifs took 1656 msec; answer was 500000000
Unpredictable ifs took 0 msec; answer was 500000000
Same without ifs took 1969 msec; answer was 500000000
Unpredictable ifs took 0 msec; answer was 500000000

他还发布了这样的 -O3 优化结果:

Puzzling measurements:
Unpredictable ifs took 1890 msec; answer was 500000000
Unpredictable ifs took 2516 msec; answer was 500000000
Same without ifs took 1422 msec; answer was 500000000
Unpredictable ifs took 2516 msec; answer was 500000000

现在我有问题。这是怎么回事?

更具体地说......一个固定的功能怎么会花费如此不同的时间?我的代码有问题吗?英特尔处理器有什么棘手的问题吗?编译器是否在做一些奇怪的事情?可能是因为 32 位代码在 64 位处理器上运行?

感谢关注!

编辑: 我接受 g++ -O1 只是在其他 2 个调用中重用返回值。我也接受 g++ -O2 和 g++ -O3 存在缺陷,导致优化被排除在外。测量速度的显着差异(450% !!!)似乎仍然很神秘。

我查看了 VS2010 生成的代码的反汇编。它内联 unpredictableIfs 3 次。内联代码非常相似。循环是一样的。它没有内联 noIfs。它确实推出了一点 noIfs。一次迭代需要 4 个步骤。 noIfs 像写的一样计算,而 unpredictableIfs 使用 jne 跳过增量。

最佳答案

使用 -O1,gcc-4.7.1 只调用一次 unpredictableIfs 并重用结果,因为它认识到它是一个纯函数,所以结果将是每次调用都一样。 (我的确实如此,通过查看生成的程序集进行验证。)

使用更高的优化级别,函数是内联的,编译器不再识别它是相同的代码,因此每次在源代码中出现函数调用时都会运行它。

除此之外,我的 gcc-4.7.1 在使用 -O1-O2 时最好处理 unpredictableIfs (除了重用问题,两者都产生相同的代码),而 noIfs-O3 处理 much 更好。然而,相同代码的不同运行之间的时间在这里是一致的 - 等于或不同 10 毫秒(clock 的粒度),所以我不知道是什么导致 的时间大不相同您为 -O3 报告的不可预测的Ifs

使用 -O2unpredictableIfs 的循环与使用 -O1 生成的代码相同(寄存器交换除外):

.L12:
    movl    %eax, %ecx
    andl    $1073741826, %ecx
    cmpl    $1, %ecx
    adcl    $0, %edx
    addl    $1, %eax
    cmpl    $1000000000, %eax
    jne .L12

对于 noIfs 也是类似的:

.L15:
    xorl    %ecx, %ecx
    testl   $1073741826, %eax
    sete    %cl
    addl    $1, %eax
    addl    %ecx, %edx
    cmpl    $1000000000, %eax
    jne .L15

在哪里

.L7:
    testl   $1073741826, %edx
    sete    %cl
    movzbl  %cl, %ecx
    addl    %ecx, %eax
    addl    $1, %edx
    cmpl    $1000000000, %edx
    jne .L7

使用 -O1。两个循环的运行时间相似,unpredictableIfs 快一点。

使用 -O3unpredictableIfs 的循环变得更糟,

.L14:
    leal    1(%rdx), %ecx
    testl   $1073741826, %eax
    cmove   %ecx, %edx
    addl    $1, %eax
    cmpl    $1000000000, %eax
    jne     .L14

对于 noIfs(包括这里的设置代码),它变得更好:

    pxor    %xmm2, %xmm2
    movq    %rax, 32(%rsp)
    movdqa  .LC3(%rip), %xmm6
    xorl    %eax, %eax
    movdqa  .LC2(%rip), %xmm1
    movdqa  %xmm2, %xmm3
    movdqa  .LC4(%rip), %xmm5
    movdqa  .LC5(%rip), %xmm4
    .p2align 4,,10
    .p2align 3
.L18:
    movdqa  %xmm1, %xmm0
    addl    $1, %eax
    paddd   %xmm6, %xmm1
    cmpl    $250000000, %eax
    pand    %xmm5, %xmm0
    pcmpeqd %xmm3, %xmm0
    pand    %xmm4, %xmm0
    paddd   %xmm0, %xmm2
    jne .L18

.LC2:
    .long   0
    .long   1
    .long   2
    .long   3
    .align 16
.LC3:
    .long   4
    .long   4
    .long   4
    .long   4
    .align 16
.LC4:
    .long   1073741826
    .long   1073741826
    .long   1073741826
    .long   1073741826
    .align 16
.LC5:
    .long   1
    .long   1
    .long   1
    .long   1

它一次计算四次迭代,因此 noIfs 的运行速度几乎是当时的四倍。

关于c++ - 难以衡量 C/C++ 性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14965553/

相关文章:

c++ - 使用散列合并全局内存写入

c++ - 有没有办法从专门的构造函数调用模板构造函数?

c - 从链表中搜索并删除节点

c++ - 我应该更喜欢在 C++ 代码中使用小类型的 int(int8 和 int16)吗?

mysql - 在 mysql 中,NOT EXISTS 函数的性能比 UNION 高得多吗?

c++ - 在命名空间 std 之外专门化 std::hash 是否不安全?

android - Android 上使用 Tensorflow 的 C++ 库

java - 如何使用 AceUnit?

c - 函数声明 : K&R vs ANSI

python - 为什么 "map"版本的ThreeSum这么慢?