假设我有一个布尔函数,它接收一个随机数作为其参数,然后如果随机数为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;
}
答案 0 :(得分:3)
问题是以下哪一项功能更有效?
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 -O2
与GCC)。但请详细了解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:
对于函数f2:
似乎当返回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);
}