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次操作

这是一个简单的程序,输出这2条指令的执行时间,您可以修改a、b、c和d,并将指令的编号作为参数传递

#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)