有一段需要重复运行多次的代码片段:
if(d < 254) {
if(d < 252)
return data[d];
return random_bit() ? data[d] : -data[d];
}
// other code
d 是一个 uint8_t 随机数,每次运行都随机产生。这段代码不管是执行哪个 return 都需要两次 if 判断,于是我把代码优化成了这样:
if(d < 252)
return data[d];
if(d < 254)
return random_bit() ? data[d] : -data[d];
// other code
这样将会以非常大的概率(252/256)只进行一次 if 判断,应该会比上面的代码更快。
代码使用 -Ofast 编译,按理来说这两段代码对编译器来说应该是一模一样的,GCC 应该会产生相同的机器码。但是实际上两段机器指令并不一样。
嵌套 if 反编译代码:
0x00000000004014d0 <+0>: push %r13
0x00000000004014d2 <+2>: push %r12
0x00000000004014d4 <+4>: mov %rdi,%r12
0x00000000004014d7 <+7>: push %rbp
0x00000000004014d8 <+8>: push %rbx
0x00000000004014d9 <+9>: sub $0x18,%rsp
0x00000000004014dd <+13>: mov 0x200(%rdi),%rdx
0x00000000004014e4 <+20>: lea 0x1(%rdx),%rax
0x00000000004014e8 <+24>: movzbl (%rdi,%rdx,1),%ebx
0x00000000004014ec <+28>: mov %rax,0x200(%rdi)
0x00000000004014f3 <+35>: cmp $0x200,%rax
0x00000000004014f9 <+41>: je 0x401740 <function+624>
0x00000000004014ff <+47>: movzbl %bl,%ebp
0x0000000000401502 <+50>: cmp $0xfd,%ebp ; 与 254 比较
0x0000000000401508 <+56>: jg 0x401530 <function+96>
0x000000000040150a <+58>: cmp $0xfb,%ebp ; 与 252 比较
0x0000000000401510 <+64>: jg 0x401650 <function+384>
0x0000000000401516 <+70>: movsbl 0x405460(%rbp),%eax
0x000000000040151d <+77>: add $0x18,%rsp
0x0000000000401521 <+81>: pop %rbx
0x0000000000401522 <+82>: pop %rbp
0x0000000000401523 <+83>: pop %r12
0x0000000000401525 <+85>: pop %r13
0x0000000000401527 <+87>: ret
可以看到如果小于等于 252 就会就地返回。
并列 if 反编译代码:
0x00000000004014d0 <+0>: push %r13
0x00000000004014d2 <+2>: push %r12
0x00000000004014d4 <+4>: push %rbp
0x00000000004014d5 <+5>: push %rbx
0x00000000004014d6 <+6>: mov %rdi,%rbx
0x00000000004014d9 <+9>: sub $0x18,%rsp
0x00000000004014dd <+13>: mov 0x200(%rdi),%rax
0x00000000004014e4 <+20>: lea 0x1(%rax),%rdx
0x00000000004014e8 <+24>: movzbl (%rdi,%rax,1),%ebp
0x00000000004014ec <+28>: mov %rdx,0x200(%rdi)
0x00000000004014f3 <+35>: cmp $0x200,%rdx
0x00000000004014fa <+42>: je 0x401720 <function+592>
0x0000000000401500 <+48>: movzbl %bpl,%r13d
0x0000000000401504 <+52>: cmp $0xfb,%r13d ; 与 252 比较
0x000000000040150b <+59>: jle 0x4016b0 <function+480>
0x0000000000401511 <+65>: mov %ebp,%eax ; 下面是 random_bit 的内联展开
0x0000000000401513 <+67>: xor %ebp,%ebp
0x0000000000401515 <+69>: mov 0x5ebf4(%rip),%rdx
0x000000000040151c <+76>: movabs $0x4000000000000000,%r12
0x0000000000401526 <+86>: and $0x1,%eax
0x0000000000401529 <+89>: cmp $0xfd,%r13d ; 与 254 比较
0x0000000000401530 <+96>: jg 0x4015c1 <function+241>
0x0000000000401536 <+102>: test %rdx,%rdx
0x0000000000401539 <+105>: je 0x401730 <function+608>
0x000000000040153f <+111>: shr %rdx
0x0000000000401542 <+114>: mov %rdx,0x5ebc7(%rip)
0x0000000000401549 <+121>: mov 0x5ebb8(%rip),%rdx
0x0000000000401550 <+128>: mov %rdx,%rax
0x0000000000401553 <+131>: shr %rax
0x0000000000401556 <+134>: mov %rax,0x5ebab(%rip)
0x000000000040155d <+141>: movsbl 0x405460(%r13),%eax
0x0000000000401565 <+149>: mov %eax,%ecx
0x0000000000401567 <+151>: neg %ecx
0x0000000000401569 <+153>: and $0x1,%edx
0x000000000040156c <+156>: cmove %ecx,%eax
0x000000000040156f <+159>: add $0x18,%rsp
0x0000000000401573 <+163>: pop %rbx
0x0000000000401574 <+164>: pop %rbp
0x0000000000401575 <+165>: pop %r12
0x0000000000401577 <+167>: pop %r13
0x0000000000401579 <+169>: ret
这段代码如果小于等于 252 就会大跳到 function+480 再返回,虽然大概率只进行一次 if 判断就返回,但是由于有一个大跳,速度会比嵌套 if 慢。
应该如何理解编译器会进行这样的优化呢?按理来说不应该有这个大跳才对。
相似问题