01 /*********************************
02 * File Name: func.c
03 *********************************/
04 0: 55 push %ebp
05 1: 89 e5 mov %esp,%ebp
06 int func (int x) 3: 8b 45 08 mov 0x8(%ebp),%eax
07 { 6: 85 c0 test %eax,%eax
08 if (x == 0) 8: 75 07 jne 11
09 { a: 5d pop %ebp
10 x += 5; b: b8 05 00 00 00 mov $0x5,%eax
11 } 10: c3 ret
12 else
13 {
14 x += 6; 11: 5d pop %ebp
15 } 12: 83 c0 06 add $0x6,%eax
16
17 return (x); 15: c3 ret
18 }
這裡面牽涉到一點,一般在user mode或者一般使用者、programmer是感受不到這差異,就是branch導致的pipeline失效...
所謂brach導致的pipeline失效是指當CPU在執行程式時,CPU會將執行instruction的過程分為幾個步驟...Fetch, Decode, Execute, Write back
clock cycle
Fetxh Decode Execute Write Baxk
I0 XX
I1 XX
I2 XX
I3 XX
I4 (Completed)
以上PCU都是根據Instruction在記憶體中的連續位址排列,循序由CPU做不同階段的處理。
但是....C語言中當遇到branch時,比方說,此篇C語言例子當中(x值不為0)...
I3是一個JNE指令,且ZF(Zero flag)為0
I2, I1, I0指令為處理 x += 5;
I3執行完之後應當執行x += 6;而不是執行pipeline中的x += 5;
所以I2, I1, I0指令於pipeline是沒用的
這種pipeline的預測失誤,稱做為Hazard
(Hazard分好幾種)
為了避免此pipeline的浪費,我們於coding時可以先考慮哪一種case被執行的機率較高...
可以緊接著在 if (x == 0) 之後。
但一般programmer並無此習慣、概念,或者說是這方面管理作的很差,GCC提供了__builtin_expect()函數,利用此函數告訴compile哪一種狀況發生的機率比較叫高,且不影響執行結果...
/****************
* CODE A
****************/
08 if (__builtin_expect(x, 0))
09 {
10 x += 5;
11 }
12 else
13 {
14 x += 6
15 }
以上同等於...
/****************
* CODE B
****************/
08 if (x)
09 {
10 x += 5;
11 }
12 else
13 {
14 x += 6
15 }
若x不為0時,執行x += 5; 反之執行x += 6;
但是CODE A中__builtin_expect(x, 0)表示...
x的值為0的機率較高,所以告訴編譯器x += 6的敘述最好安排於if (x)之後,以減少branch的發生,以提高CPU的使用率。
這邊做一個比較
$ cat func.c
/**************************
* CODE 001
**************************/
int func(int x)
{
if (__builtin_expect(x, 1)) /* 注意 */
{
x += 5;
}
else
{
x += 6;
}
return x;
}
$
$ gcc -O2 -c func.c -o func.o
$
$ objdump -S func.o
func.o: file format elf32-i386
Disassembly of section .text:
00000000 :
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 8b 45 08 mov 0x8(%ebp),%eax
6: 85 c0 test %eax,%eax
8: 74 05 je f /* 注意 */
a: 5d pop %ebp
b: 83 c0 05 add $0x5,%eax /* x += 5 */
e: c3 ret
f: 5d pop %ebp
10: b8 06 00 00 00 mov $0x6,%eax /* x += 6 */
15: c3 ret
另一個檔案
$ cat func.c
/**************************
* CODE 002
**************************/
int func(int x)
{
if (__builtin_expect(x, 0)) /* 注意 */
{
x += 5;
}
else
{
x += 6;
}
return x;
}
$
$ gcc -O2 -c func.c -o func.o
$
$ objdump -S func.o
func.o: file format elf32-i386
Disassembly of section .text:
00000000 :
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 8b 45 08 mov 0x8(%ebp),%eax
6: 85 c0 test %eax,%eax
8: 75 07 jne 11 /* 注意 */
a: 5d pop %ebp
b: b8 06 00 00 00 mov $0x6,%eax /* x += 6 */
10: c3 ret
11: 5d pop %ebp
12: 83 c0 05 add $0x5,%eax /* x += 5 */
15: c3 ret
No comments:
Post a Comment