【问题标题】:why comparing char variables twice is faster than comparing short variables once为什么比较 char 变量两次比比较短变量一次快
【发布时间】:2013-12-25 09:48:06
【问题描述】:

我认为一个比较必须比两个快。但是经过我的测试,我发现在调试模式下短比较快一点,而在发布模式下,字符比较更快。我想知道真正的原因。

以下是测试代码和测试结果。我写了两个简单的函数,func1() 使用两个字符比较,func2() 使用一个短比较。主函数返回临时返回值以避免编译优化忽略我的测试代码。我的编译器是 GCC 4.7.2,CPU Intel® Xeon® CPU E5-2430 0 @ 2.20GHz (VM)。

inline int func1(unsigned char word[2])
{
        if (word[0] == 0xff && word[1] == 0xff)
                return 1;
        return 0;
}

inline int func2(unsigned char word[2])
{
        if (*(unsigned short*)word == 0xffff)
                return 1;
        return 0;
}

int main()
{
        int n_ret = 0;
        for (int j = 0; j < 10000; ++j)
                for (int i = 0; i < 70000; ++i)
                        n_ret += func2((unsigned char*)&i);
        return n_ret;
}

调试模式:

          func1      func2
real    0m3.621s    0m3.586s
user    0m3.614s    0m3.579s
sys     0m0.001s    0m0.000s

发布模式:

          func1      func2
real    0m0.833s    0m0.880s
user    0m0.831s    0m0.878s
sys     0m0.000s    0m0.002s

func1版的汇编代码:

        .cfi_startproc
        movl    $10000, %esi
        xorl    %eax, %eax
        .p2align 4,,10
        .p2align 3
.L6:
        movl    $1, %edx
        xorl    %ecx, %ecx
        .p2align 4,,10
        .p2align 3
.L8:
        movl    %edx, -24(%rsp)
        addl    $1, %edx
        addl    %ecx, %eax
        cmpl    $70001, %edx
        je      .L3
        xorl    %ecx, %ecx
        cmpb    $-1, -24(%rsp)
        jne     .L8
        xorl    %ecx, %ecx
        cmpb    $-1, -23(%rsp)
        sete    %cl
        jmp     .L8
        .p2align 4,,10
        .p2align 3
.L3:
        subl    $1, %esi
        jne     .L6
        rep
        ret
        .cfi_endproc

func2版的汇编代码:

        .cfi_startproc
        movl    $10000, %esi
        xorl    %eax, %eax
        .p2align 4,,10
        .p2align 3
.L4:
        movl    $1, %edx
        xorl    %ecx, %ecx
        jmp     .L3
        .p2align 4,,10
        .p2align 3
.L7:
        movzwl  -24(%rsp), %ecx
.L3:
        cmpw    $-1, %cx
        movl    %edx, -24(%rsp)
        sete    %cl
        addl    $1, %edx
        movzbl  %cl, %ecx
        addl    %ecx, %eax
        cmpl    $70001, %edx
        jne     .L7
        subl    $1, %esi
        jne     .L4
        rep
        ret
        .cfi_endproc

【问题讨论】:

  • 1) 是否一直更快?或者这只是一个小故障? 2) 向我们展示生成的汇编代码。
  • 嗯...那些时间远远超出了测量的不确定性...此外,调试构建(==没有优化)没有有意义的时间/复杂性配置文件。此外,第二个函数调用了未定义的行为,因此甚至不值得推理。
  • 您必须展示编译器在每种情况下生成的汇编代码,才能使对该主题的任何讨论有意义。 C 代码没有内在性能。 (从技术上讲,汇编代码也不能没有目标处理器,但我们假设一个现代的广泛可用的处理器)
  • 这些数字几乎相同。一次关闭会受到操作系统切换等的影响。至少运行几百次。
  • @PascalCuoq 事实并非如此,因为&amp;i 是int *。

标签: c++ c performance assembly


【解决方案1】:

在 GCC 4.6.3 中,第一段和第二段代码的代码不同,如果运行足够长的时间,func1 选项的运行时间会明显变慢。不幸的是,由于您的运行时间很短,两者在时间上看起来相似。

将外循环增加 10 倍意味着 func2 大约需要 6 秒,而 func1 大约需要 10 秒。这是使用gcc -std=c99 -O3 编译代码。

我预计,主要区别在于 && 语句引入的额外分支。额外的xorl %ecx, %ecx 并没有多大帮助(我得到了相同的结果,尽管我的代码在标签名称方面看起来略有不同)。

编辑:我确实尝试过使用 and 而不是分支来提出无分支解决方案,但编译拒绝内联函数,因此需要 30 秒而不是 10 秒。

基准运行于:

AMD Phenom(tm) II X4 965

以 3.4 GHz 运行。

【讨论】:

  • 我测试了 10x 外循环,两个函数的成本时间都增加了 10 倍,分别为 8.35s 和 8.79s。
  • 显然,你的处理器运行方式与我的不同——不管是在 6 秒内运行 func2 版本的处理器还是在 8 秒内运行 func1 的处理器更好......
  • 既然我们一直在走这条路,你们能用你们的 CPU 类型更新你的帖子吗?
  • 我不知道and 是什么,但是作为迈向无分支的第一步,用&amp; 代替&amp;&amp; 怎么样?
  • 我怀疑分支预测器是这里的一个因素。如果我将第一个函数更改为进行单字节比较(注释掉 &amp;&amp; word[1] == 0xFF),我的 Phenom II 上的两个函数之间的时序相同(实际上与 Mats 的处理器相同)。
猜你喜欢
  • 1970-01-01
  • 2020-07-07
  • 2016-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多