在做實驗之前,先提到3點
1. 這是延續之前討論課題
X86 Instruction - TEST
X86 TEST, Branch Predication, GCC __builtin_expect() function
branch於不同CPU的作法(X86, ARM, SPARC)比較
2. 參考資料來源
Wikipedia Hazard (computer architecture)
3. 做此實驗須注意事項...
因為發生Hazard的條件太多,比方說在一般OS執行狀況下,有time slice/context switch機制,每發生一次context switch,Hazard就發生了
所以實驗環境要單純
不能有不能控制的狀況,比方不能有interrupt/trap產生
要有實驗組、對照組:兩組不一樣的地方只能在branch發生的地方出現
所以,以我現有的設備,我選擇Leon2 CPU,配合sparc-elf-3.4.4 toolchain...
現有LEON2 CPU速度夠慢,比較容易觀察到結果
它有完整的POSIX API支援
且是OS-less,但有完整的start-up code
可以用C語言很容易做I/O部分
不會有任何意外的interrupt產生
利用sparc-elf-3.4.4 toochain配合組合語言,我可以很精確的控制到實驗組跟對照組不一樣的地方只有branch部分,其餘code部分的行為模式是一模一樣。
首先先列出Leon2 CPU pipeline架構...
它是5級pipeline架構
分別是Fetch, Decode, Execute, Memory, Write,如下圖
於code比較部份,我用組合語言寫了兩個function,其pseudo code...
/* func 1 */
int func1(int x)
{
if (x != 0)
return 1;
else
return 2;
}
/* func 2 */
int func2(int x)
{
if (x == 0)
return 2;
else
return 1;
}
呼叫這兩個function的main函數pseudo code...
20 /*
21 * This will cause less branch miss
22 */
23 times (&time_info);
24 time_b = (long) time_info.tms_utime;
25
26 seed = 0;
27 for (i = 0; i < NUM_1; i++)
28 {
29 seed += func1(i);
30 }
31
32 times (&time_info);
33 time_e = (long) time_info.tms_utime;
34
35 printf("func1...%d, %d\n", seed, time_e - time_b);
36
37 /*
38 * This will cause more branch miss
39 */
40 times (&time_info);
41 time_b = (long) time_info.tms_utime;
42
43 seed = 0;
44 for (i = 0; i < NUM_1; i++)
45 {
46 seed += func2(i);
47 }
48
49 times (&time_info);
50 time_e = (long) time_info.tms_utime;
51
52 printf("func2...%d, %d\n", seed, time_e - time_b);
27~30以及44~47行為測試主要部份,呼叫func1(), func2()的參數大部分不為0,所以func1()為實驗組,func2為對照組。 其整個編譯過程...
~/hazard $ ls -l
total 9
-rw-r--r-- 1 LungsWu None 287 Oct 6 13:09 Makefile
-rw-r--r-- 1 LungsWu None 1250 Oct 6 12:59 func.S
-rw-r--r-- 1 LungsWu None 945 Oct 6 12:29 main.c
~/hazard $ make
sparc-elf-gcc -c -mv8 -msoft-float main.c -o main.o
sparc-elf-gcc -c -mv8 -msoft-float func.S -o func.o
sparc-elf-gcc -mv8 -msoft-float main.o func.o -o hazard
~/hazard $
再來用sparc-elf-objdump觀察實驗組func1()... 14~24...x不為0,沒發生branch 28~38...x為0,發生branch
00000000 :
0: 9d e3 bf 98 save %sp, -104, %sp
4: f0 27 a0 44 st %i0, [ %fp + 0x44 ]
8: c2 07 a0 44 ld [ %fp + 0x44 ], %g1
c: 80 a0 60 00 cmp %g1, 0
10: 02 80 00 06 be 28
14: 82 10 20 01 mov 1, %g1
18: b0 10 00 01 mov %g1, %i0
1c: 81 c7 e0 08 ret
20: 81 e8 00 00 restore
24: 01 00 00 00 nop
00000028 :
28: 82 10 20 02 mov 2, %g1 ! 2
2c: b0 10 00 01 mov %g1, %i0
30: 81 c7 e0 08 ret
34: 81 e8 00 00 restore
38: 01 00 00 00 nop
再來用sparc-elf-objdump觀察對照組func2()... 50~60...x為0,發生branch 64~74...x不為0,沒發生branch
0000003c :
3c: 9d e3 bf 98 save %sp, -104, %sp
40: f0 27 a0 44 st %i0, [ %fp + 0x44 ]
44: c2 07 a0 44 ld [ %fp + 0x44 ], %g1
48: 80 a0 60 00 cmp %g1, 0
4c: 12 80 00 06 bne 64
50: 82 10 20 02 mov 2, %g1
54: b0 10 00 01 mov %g1, %i0
58: 81 c7 e0 08 ret
5c: 81 e8 00 00 restore
60: 01 00 00 00 nop
00000064 :
64: 82 10 20 01 mov 1, %g1 ! 1
68: b0 10 00 01 mov %g1, %i0
6c: 81 c7 e0 08 ret
70: 81 e8 00 00 restore
74: 01 00 00 00 nop
func1()跟func2控制到流程一模一樣,只有cmp之後的be,bne發生branch的條件不一樣,所以執行結果....
func2...5000001, 337
func1...5000001, 328
func2...5000001, 337
func1...5000001, 328
func1...5000001, 328
func2...5000001, 337
func1...5000001, 328
func2...5000001, 337
func1()果然比func1慢了(337-328)/328=0.027439左右,每次運算結果一樣精確一樣。
No comments:
Post a Comment