一个程序的效率,它检查变量与另一个变量的范围,逐个检查值?

时间:2017-06-06 07:57:50

标签: c performance

假设我有一个布尔函数,它接收一个随机数作为其参数,然后如果随机数为200,201或202则返回 True 并返回 False 对于其他价值观。

问题是以下哪一项功能更有效?

F1:

bool f1(int number) {
    if (number >= 200 && number <= 202)
        return true;
    return false;
}

F2:

bool f2(int number) {
    if (number == 200 || number == 201 || number == 202)
        return true;
    return false;
}

4 个答案:

答案 0 :(得分:3)

  

问题是以下哪一项功能更有效?

C11标准(阅读n1570)并不关心(或谈论)效率。

optimizing compiler可以为这两个函数生成相同的代码(其中有几个函数)。

Linux / x86-64上的My GCC 7.1编译器生成与gcc -fverbose-asm -O2 -S相同的代码:

         .text
         .p2align 4,,15
         .globl  f1
         .type   f1, @function
 f1:
 .LFB0:
         .cfi_startproc
 # abraham.c:3:     if (number >= 200 && number <= 202)
         subl    $200, %edi      #, tmp92
         cmpl    $2, %edi        #, tmp92
         setbe   %al     #, tmp93
 # abraham.c:6: }
         ret
         .cfi_endproc
 .LFE0:
         .size   f1, .-f1
         .p2align 4,,15
         .globl  f2
         .type   f2, @function
 f2:
 .LFB3:
         .cfi_startproc
         subl    $200, %edi      #, tmp92
         cmpl    $2, %edi        #, tmp92
         setbe   %al     #, tmp93
         ret
         .cfi_endproc
 .LFE3:
         .size   f2, .-f2
         .ident  "GCC: (Debian 7.1.0-2) 7.1.0"
         .section        .note.GNU-stack,"",@progbits

BTW clang-4.0 -fverbose-asm -S -O2为两个函数生成相同的代码,但与gcc不同:

          .type   f1,@function
  f1:                                     # @f1
          .cfi_startproc
  # BB#0:
          addl    $-200, %edi
          cmpl    $3, %edi
          setb    %al
          retq
  .Lfunc_end0:
          .size   f1, .Lfunc_end0-f1
          .cfi_endproc

如果性能对您很重要,我建议在一些常见的标题中将这两个函数定义为static inline

如果您真的关心性能,基准(在要求编译器进行优化后,例如gcc -Wall -O2GCC)。但请详细了解premature optimization,尤其是fallacy of premature optimization。请注意,在没有启用优化的情况下询问性能是矛盾的。

大多数时候,你应该选择更具可读性的东西。

答案 1 :(得分:2)

我对它进行了基准测试(你应该学习怎么做):

#!/bin/sh -e
cat > bench.c <<EOF
#include <stdio.h>
#include <stdlib.h>

_Bool f1(int number) { return (number >= 200 && number <= 202); }
_Bool f2(int number) { return (number == 200 || number == 201 || number == 202); }

int main(int c, char **v)
{
    int it = c>1 ? atoi(v[1]) : 10000000000;
    int cnt=0;
    for(int j=0; j<10;j++)
    for(int i=0;i<it;i++){
#ifdef F2
        cnt+=f2(i);
#else
        cnt+=f1(i);
#endif
    }
    printf("%d\n", cnt);

}
EOF

gcc -O3 bench.c
./a.out 1
time ./a.out
gcc -DF2 -O3 bench.c
./a.out 1
time ./a.out

无法衡量统计上显着的差异。 然后我检查了生成的assembly,gcc为这两种情况生成了相同的输出,从-O1开始(clang不是那么聪明):

f1:
        subl    $200, %edi
        cmpl    $2, %edi
        setbe   %al
        ret
f2:
        subl    $200, %edi
        cmpl    $2, %edi
        setbe   %al
        ret

(看起来像一个非常简洁的优化技巧)

所以答案是通常的:做更可读的事情并将优化留给优化器,直到你测量并发现它没有尽可能地完成它的工作。

答案 2 :(得分:1)

只需查看汇编代码,我们只会根据 if

进行一些统计
bool f1(int number) {
011D9160  push        ebp  
011D9161  mov         ebp,esp  
011D9163  sub         esp,0C0h  
011D9169  push        ebx  
011D916A  push        esi  
011D916B  push        edi  
011D916C  lea         edi,[ebp-0C0h]  
011D9172  mov         ecx,30h  
011D9177  mov         eax,0CCCCCCCCh  
011D917C  rep stos    dword ptr es:[edi]  
        if (number >= 200 && number <= 202)
011D917E  cmp         dword ptr [number],0C8h  
011D9185  jl          f1+34h (011D9194h)  
011D9187  cmp         dword ptr [number],0CAh  
011D918E  jg          f1+34h (011D9194h)  
            return true;
011D9190  mov         al,1  
011D9192  jmp         f1+36h (011D9196h)  
        return false;
011D9194  xor         al,al  }   


bool f2(int number) {
011D91B0  push        ebp  
011D91B1  mov         ebp,esp  
011D91B3  sub         esp,0C0h  
011D91B9  push        ebx  
011D91BA  push        esi  
011D91BB  push        edi  
011D91BC  lea         edi,[ebp-0C0h]  
011D91C2  mov         ecx,30h  
011D91C7  mov         eax,0CCCCCCCCh  
011D91CC  rep stos    dword ptr es:[edi]  
        if (number == 200 || number == 201 || number == 202)
011D91CE  cmp         dword ptr [number],0C8h  
011D91D5  je          f2+39h (011D91E9h)  
011D91D7  cmp         dword ptr [number],0C9h  
011D91DE  je          f2+39h (011D91E9h)  
011D91E0  cmp         dword ptr [number],0CAh  
011D91E7  jne         f2+3Dh (011D91EDh)  
            return true;
011D91E9  mov         al,1  
011D91EB  jmp         f2+3Fh (011D91EFh)  
        return false;
011D91ED  xor         al,al  
    }

对于功能f1:

  • 当数字等于200时,函数f1调用cmp,jl,cmp,jg,mov,jmp 6 逻辑操作说明。
  • 当数字等于201时,函数f1调用cmp,jl,cmp,jg,mov,jmp 6 逻辑运算指令。
  • 当数字等于202时,函数f1调用cmp,jl,cmp,jg,mov,jmp 6 逻辑运算指令。
  • 当数字小于200时,函数f1调用cmp,jl,xor 3 逻辑运算指令。
  • 当数字大于202时,函数f1调用cmp,jl,cmp,jg,xor 5 逻辑运算指令。

对于函数f2:

  • 当数字等于200时,函数f2调用cmp,je,mov,jmp 4 逻辑操作说明。
  • 当数字等于201时,函数f2调用cmp,je,cmp,je,mov,jmp 6 逻辑运算指令。
  • 当数字等于202时,函数f2调用cmp,je,cmp,je,cmp,jne,mov,jmp 8 逻辑运算指令。
  • 对于其他数字,函数f2调用cmp,je,cmp,je,cmp,jne,xor 7 逻辑运算指令。

似乎当返回true时,性能平均相同,因为6 + 6 + 6 = 4 + 6 + 8。 但是当返回false时,f1优于f2。

答案 3 :(得分:-1)

还有两个可供您衡量的选项

#include <stdbool.h>
bool f3(int n) {
    if (n < 200) return 0;
    if (n > 202) return 0;
    return 1;
}

#include <stdbool.h>
bool f4(int n) {
    return (n >= 200) * (n <= 202);
}