C 算术与布尔运算
我在某个论坛上遇到了这段代码:C 算术与布尔运算,c,boolean,arithmetic-expressions,C,Boolean,Arithmetic Expressions,我在某个论坛上遇到了这段代码: 如果(a*b*c*d==0)… 店主声称这比你想象的要快一点 if (a == 0 || b == 0 || c == 0 || d == 0) if(a==0 | | b==0 | | c==0 | | d==0) 这些变量定义为: inta、b、c、d 其绝对值保证小于或等于100。(因此我们可以忽略溢出的可能性) 如果我们只是忽略可读性,而只关注性能,那么这种说法真的正确吗 在我看来,第二种方法实际上可能更快,因为有时你可以利用“短路”。但是,我知道什么
如果(a*b*c*d==0)…
店主声称这比你想象的要快一点
if (a == 0 || b == 0 || c == 0 || d == 0)
if(a==0 | | b==0 | | c==0 | | d==0)
这些变量定义为:
inta、b、c、d代码>
其绝对值保证小于或等于100。(因此我们可以忽略溢出的可能性)
如果我们只是忽略可读性,而只关注性能,那么这种说法真的正确吗
在我看来,第二种方法实际上可能更快,因为有时你可以利用“短路”。但是,我知道什么 是当if指令失败时,因为在这种情况下,我们在第二条指令中最多执行4次比较(操作)
,而对于第一条指令,我们总是执行4次操作
编辑:解释
第二条if指令始终比第一条指令快:
假设:a=1,b=2,c=0和d=4,在这种情况下:
- 对于第一条指令:我们有3次乘法,一次比较=4次运算
- 对于第二条if指令:我们将a与0(结果KO)进行比较,然后将b与0(同样是KO)进行比较,并将c与0(确定)=3次操作
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/* This is a test program to demonstrate that the second if is faster always than the first one*/
int main(int argc, char **argv)
{
int i;
int a = 1;
int b = 2;
int c = 0;
int d = 4;
int instruction_number;
clock_t begin, end;
double time_spent;
begin = clock();
if (argc != 2)
{
fprintf(stderr, "Usage : ./a.out if_instruction_number (1 or 2)\n");
exit(EXIT_FAILURE);
}
instruction_number = atoi(argv[1]);
for (i = 1; i < 100000; i++)
{
switch (instruction_number)
{
case 1:
fprintf(stdout, "First if instruction : \n");
if (a * b * c * d == 0)
fprintf(stdout, "1st instruction\n");
break;
case 2:
fprintf(stdout, "Second if instruction : \n");
if (a == 0 || b == 0 || c == 0 || d == 0)
fprintf(stdout, "2nd instruction\n");
break;
default:
break;
}
}
end = clock();
time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
fprintf(stdout, "Time to accomplish %d instruction ---> %f\n", instruction_number, time_spent);
return 0;
}
#包括
#包括
#包括
/*这是一个测试程序,用于证明第二个if始终比第一个if快*/
int main(int argc,字符**argv)
{
int i;
INTA=1;
int b=2;
int c=0;
int d=4;
int指令号;
时钟开始,结束;
花费的时间加倍;
开始=时钟();
如果(argc!=2)
{
fprintf(标准,“用法:./a.out if_指令\u编号(1或2)\n”);
退出(退出失败);
}
指令号=atoi(argv[1]);
对于(i=1;i<100000;i++)
{
开关(指令号)
{
案例1:
fprintf(stdout,“第一个if指令:\n”);
如果(a*b*c*d==0)
fprintf(标准输出,“第一条指令”);
打破
案例2:
fprintf(stdout,“第二个if指令:\n”);
如果(a==0 | | b==0 | | c==0 | | d==0)
fprintf(标准输出,“第二条指令”);
打破
违约:
打破
}
}
结束=时钟();
花费的时间=(双倍)(结束-开始)/每秒时钟;
fprintf(标准输出,“完成%d条指令的时间-->%f\n”,指令编号,所用时间);
返回0;
}
希望这有帮助
问候。这两者并不等同。例如,在我的机器上(32位x86 MSVC),如果a、b、c和d都等于
0x100
,则第一个测试将通过,但第二个条件将不通过
还要注意乘法是一个代价很高的操作,所以第一个版本不一定会更快
编辑:为第一个版本生成的代码:
00401000 8B 44 24 04 mov eax,dword ptr [esp+4]
00401004 0F AF 44 24 08 imul eax,dword ptr [esp+8]
00401009 0F AF 44 24 0C imul eax,dword ptr [esp+0Ch]
0040100E 0F AF 44 24 10 imul eax,dword ptr [esp+10h]
00401013 85 C0 test eax,eax
00401015 75 07 jne f1+1Eh (40101Eh)
00401017 ...
为第二个版本生成的代码:
00401020 83 7C 24 04 00 cmp dword ptr [esp+4],0
00401025 74 15 je f2+1Ch (40103Ch)
00401027 83 7C 24 08 00 cmp dword ptr [esp+8],0
0040102C 74 0E je f2+1Ch (40103Ch)
0040102E 83 7C 24 0C 00 cmp dword ptr [esp+0Ch],0
00401033 74 07 je f2+1Ch (40103Ch)
00401035 83 7C 24 10 00 cmp dword ptr [esp+10h],0
0040103A 75 07 jne f2+23h (401043h)
0040103C ...
我的机器上的基准测试(以纳秒为单位):第一个版本的运行时间约为1.83纳秒,第二个版本的运行时间约为1.39纳秒。a、b、c和d的值在每次运行期间都没有变化,因此显然分支预测器可以预测100%的分支。如果(a*b*c*d==0)
编译为(无优化)
如果(a==0 | | b==0 | | c==0 | | d==0)编译为
cmpl $0, 16(%esp)
je .L2
cmpl $0, 20(%esp)
je .L2
cmpl $0, 24(%esp)
je .L2
cmpl $0, 28(%esp)
jne .L4
和往常一样,哪一个更快?问题是,到目前为止你尝试了什么?您是否编译和反汇编并查看发生了什么
unsigned int mfun ( unsigned int a, unsigned int b, unsigned int c, unsigned int d )
{
if ( a * b * c * d == 0 ) return(7);
else return(11);
}
unsigned int ofun ( unsigned int a, unsigned int b, unsigned int c, unsigned int d )
{
if (a == 0 || b == 0 || c == 0 || d == 0) return(7);
else return(11);
}
对于arm 1,编译器给出了
00000000 <mfun>:
0: e0010190 mul r1, r0, r1
4: e0020291 mul r2, r1, r2
8: e0110293 muls r1, r3, r2
c: 13a0000b movne r0, #11
10: 03a00007 moveq r0, #7
14: e12fff1e bx lr
00000018 <ofun>:
18: e3500000 cmp r0, #0
1c: 13510000 cmpne r1, #0
20: 0a000004 beq 38 <ofun+0x20>
24: e3520000 cmp r2, #0
28: 13530000 cmpne r3, #0
2c: 13a0000b movne r0, #11
30: 03a00007 moveq r0, #7
34: e12fff1e bx lr
38: e3a00007 mov r0, #7
3c: e12fff1e bx lr
00000000:
0:e0010190 mul r1,r0,r1
4:e0020291 mul r2、r1、r2
8:e0110293 muls r1、r3、r2
c:13a0000b移动r0,#11
10:03a00007移动量r0,#7
14:E12FF1E bx lr
00000018 :
18:e3500000 cmp r0,#0
1c:13510000 cmpne r1,#0
20:0a000004北京标准普尔38
24:e3520000 cmp r2,#0
28:13530000立方厘米r3,#0
2c:13a0000b移动r0,#11
30:03a00007移动量r0,#7
34:E12FF1E bx lr
38:e3a00007 mov r0,#7
3c:E12FF1E bx lr
因此,equals和or有短路(这本身代价很高),但最坏路径需要更长的时间,因此性能不稳定,乘法性能更具确定性,不太不稳定。通过检查,上述代码的乘法解应该更快
mips给了我这个
00000000 <mfun>:
0: 00a40018 mult a1,a0
4: 00002012 mflo a0
...
10: 00860018 mult a0,a2
14: 00002012 mflo a0
...
20: 00870018 mult a0,a3
24: 00002012 mflo a0
28: 10800003 beqz a0,38 <mfun+0x38>
2c: 00000000 nop
30: 03e00008 jr ra
34: 2402000b li v0,11
38: 03e00008 jr ra
3c: 24020007 li v0,7
00000040 <ofun>:
40: 10800009 beqz a0,68 <ofun+0x28>
44: 00000000 nop
48: 10a00007 beqz a1,68 <ofun+0x28>
4c: 00000000 nop
50: 10c00005 beqz a2,68 <ofun+0x28>
54: 00000000 nop
58: 10e00003 beqz a3,68 <ofun+0x28>
5c: 00000000 nop
60: 03e00008 jr ra
64: 2402000b li v0,11
68: 03e00008 jr ra
6c: 24020007 li v0,7
00000000:
0:00a40018 mult a1,a0
4:000012 mflo a0
...
10:00860018 mult a0,a2
14:000012 mflo a0
...
20:00870018 mult a0,a3
24:000012 mflo a0
28:10800003贝基兹a0,38
2c:00000000无
30:03e00008 jr ra
34:2402000b锂v0,11
38:03e00008 jr ra
3c:24020007 li v0,7
00000040 :
40:10800009贝基兹a0,68
44:00000000无
48:10a00007贝基兹a1,68
4c:00000000无
50:10c00005 beqz a2,68
54:00000000无
58:10e00003 beqz a3,68
5c:00000000无
60:03e00008 jr ra
64:2402000b锂v0,11
68:03e00008 jr ra
6c:24020007 li v0,7
除非分支机构的成本太高,否则equals和or看起来更快
Openrisc 32
00000000 <mfun>:
0: e0 64 1b 06 l.mul r3,r4,r3
4: e0 a3 2b 06 l.mul r5,r3,r5
8: e0 c5 33 06 l.mul r6,r5,r6
c: bc 26 00 00 l.sfnei r6,0x0
10: 0c 00 00 04 l.bnf 20 <mfun+0x20>
14: 9d 60 00 0b l.addi r11,r0,0xb
18: 44 00 48 00 l.jr r9
1c: 15 00 00 00 l.nop 0x0
20: 44 00 48 00 l.jr r9
24: 9d 60 00 07 l.addi r11,r0,0x7
00000028 <ofun>:
28: e0 e0 20 02 l.sub r7,r0,r4
2c: e0 87 20 04 l.or r4,r7,r4
30: bd 64 00 00 l.sfgesi r4,0x0
34: 10 00 00 10 l.bf 74 <ofun+0x4c>
38: e0 80 18 02 l.sub r4,r0,r3
3c: e0 64 18 04 l.or r3,r4,r3
40: bd 63 00 00 l.sfgesi r3,0x0
44: 10 00 00 0c l.bf 74 <ofun+0x4c>
48: e0 60 30 02 l.sub r3,r0,r6
4c: e0 c3 30 04 l.or r6,r3,r6
50: bd 66 00 00 l.sfgesi r6,0x0
54: 10 00 00 08 l.bf 74 <ofun+0x4c>
58: e0 60 28 02 l.sub r3,r0,r5
5c: e0 a3 28 04 l.or r5,r3,r5
60: bd 85 00 00 l.sfltsi r5,0x0
64: 0c 00 00 04 l.bnf 74 <ofun+0x4c>
68: 9d 60 00 0b l.addi r11,r0,0xb
6c: 44 00 48 00 l.jr r9
70: 15 00 00 00 l.nop 0x0
74: 44 00 48 00 l.jr r9
78: 9d 60 00 07 l.addi r11,r0,0x7
00000000:
0:e0 64 1b 06 l.mul r3、r4、r3
4:e0 a3 2b 06升亩
00000000 <mfun>:
0: 0b 12 push r11
2: 0a 12 push r10
4: 09 12 push r9
6: 09 4d mov r13, r9
8: 0b 4c mov r12, r11
a: 0a 4e mov r14, r10
c: 0c 4f mov r15, r12
e: b0 12 00 00 call #0x0000
12: 0a 4e mov r14, r10
14: 0c 49 mov r9, r12
16: b0 12 00 00 call #0x0000
1a: 0a 4e mov r14, r10
1c: 0c 4b mov r11, r12
1e: b0 12 00 00 call #0x0000
22: 0e 93 tst r14
24: 06 24 jz $+14 ;abs 0x32
26: 3f 40 0b 00 mov #11, r15 ;#0x000b
2a: 39 41 pop r9
2c: 3a 41 pop r10
2e: 3b 41 pop r11
30: 30 41 ret
32: 3f 40 07 00 mov #7, r15 ;#0x0007
36: 39 41 pop r9
38: 3a 41 pop r10
3a: 3b 41 pop r11
3c: 30 41 ret
0000003e <ofun>:
3e: 0f 93 tst r15
40: 09 24 jz $+20 ;abs 0x54
42: 0e 93 tst r14
44: 07 24 jz $+16 ;abs 0x54
46: 0d 93 tst r13
48: 05 24 jz $+12 ;abs 0x54
4a: 0c 93 tst r12
4c: 03 24 jz $+8 ;abs 0x54
4e: 3f 40 0b 00 mov #11, r15 ;#0x000b
52: 30 41 ret
54: 3f 40 07 00 mov #7, r15 ;#0x0007
58: 30 41
if ( a * b * c * d == 0 )
if (a == 0 || b == 0 || c == 0 || d == 0)