背景
在公司arm64平台上增加了spe功能,是個用於性能分析的利器,目前只是該功能實現了,但是該功能是否正確,就有待驗證了。
因為spe支持分支預測檢測功能,這個功能對優化我們系統性能來說實在太重要了,所以首當其沖的就是驗證該分支預測的准確性了。
什么是分支預測(branch prediction)
直白的說,分支預測就是指令存在多個if else判定的情況下,cpu去預測下一步即將執行哪一調指令,通過預測,優化指令執行效率,達到提高系統性能的目的。
if { ... } else if { ... } else if { .... }else ...
但是若程序中出現較多這樣的指令,分支預測的准確率就會降低,當然系統性能也會受到影響,所以通常不建議程序中存在存在過多的跳轉或者判定指令,倘若通過某種手段檢測分支預測
失敗的次數,對改進程序設計,優化程序執行效率,會有很大的借鑒意義。
怎么構造分支預測模型呢?
去構造分支預測模型不是一件容易的事,尤其是不了解代碼下指令執行的意圖,筆者思考痛苦的很久,都沒有找到較為合理的針對分支預測檢測方法,夜思冥想最后再師兄的點播下,終於有了點路子。
既然我的需要驗證分支預測的正確性,我只要構造一個程序,其分支預測永遠為100%,什么樣的程序分支預測永遠為100%,答案是順序執行的,但是順序執行還遠遠不夠,你還需要抓取其分支預測情況,所以你還需要讓程序長時間運行,有了這兩點后,我寫出了個100%的分支預測命中的程序。
int main() { while (1); return 0 }
代碼很簡單,可是你能否想象,要說服自己構造這么個東西,可整整耗費了本人一下午。
黎明前的黑暗
gcc -O0 main.c,輕輕送編譯完成,開始測試,測試branch-misses還是挺簡單的,perf stat -e branch-misses ./a.out
Performance counter stats for './a.out': 25791 branch-misses 3.307213447 seconds time elapsed 3.303546000 seconds user 0.003999000 seconds sys
出來的結果永遠是亮瞎人的狗眼,很顯然,分支預測100%是不可能了,我再次陷入了絕望,開始懷疑自己這么構造的分支預測模型是否正確了,只能進行各對比測試了,例如把鍋甩給
arm,到x86平台上去試試,發現還是一樣,哎,又是郁悶的一下午,我有點想放棄,很偶然的一次想法突然給了我思路,為啥我死循環沒有導致系統卡死?,因為我沒有指定CPU上運行,系統很有可能為了均衡,進行了調度,有了思路,我開始綁核測試。
taskset -c 1 ./a.out
可能現實總喜歡啪啪打臉.
看着運行百分之百的cpu,我再次陷入了沉思,我想放棄了。
Performance counter stats for 'CPU(s) 1': 21473 branch-misses 2.059929220 seconds time elapsed
branch-misses有增無減少,看樣子,我真的不知道該怎么處理了。
艱難的值守總會迎來希望
又事一次很隨意的操作,讓我思路徹底挖開,我在perf的時候加了一個-g指令,看看其函數調用流程。
Samples: 11K of event 'branch-misses', Event count (approx.): 2031156 Children Self Command Shared Object Symbol + 35.56% 0.00% swapper [kernel.vmlinux] [k] cpu_startup_entry ◆ + 35.56% 0.02% swapper [kernel.vmlinux] [k] do_idle ▒ + 35.01% 0.00% swapper [kernel.vmlinux] [k] secondary_start_kernel ▒ + 32.89% 10.08% swapper [kernel.vmlinux] [k] arch_cpu_idle ▒ + 22.85% 0.00% swapper [kernel.vmlinux] [k] el1_irq ▒ + 22.85% 0.00% swapper [kernel.vmlinux] [k] gic_handle_irq ▒ + 22.85% 0.00% swapper [kernel.vmlinux] [k] __handle_domain_irq ▒ + 22.85% 0.00% swapper [kernel.vmlinux] [k] irq_exit ▒ + 22.68% 7.16% swapper [kernel.vmlinux] [k] __do_softirq ▒ + 20.93% 0.00% /home/staragent libc-2.30.so [.] thread_start
好像這些都和我執行的死循環無關,看樣子,我抓取的branch-misses都不是我應用程序產生的,為了確定我的判定,我再次單獨perf抓取了死循環的調用情況。
Children Self Command Shared Object Symbol - 100.00% 27.56% a.out a.out [.] main ◆ - 91.49% main ▒ - 65.31% el0_irq ▒ gic_handle_irq ▒ __handle_domain_irq ▒ irq_exit ▒ + __do_softirq ▒ - 7.13% work_pending ▒ - do_notify_resume ▒ + 3.00% task_work_run ▒ - 8.51% _start ▒ main
看樣子思路明晰起來,這些都不是我代碼執行導致,是中斷導致,因為cpu產生了中斷,去處理別的事情,進入內核態或者別的,導致出現了branch-misses的情況。
那程序運行在用戶態,按照我這么理解,那用戶態branch-misses肯定為0咯,我們試試看。
./perf record -g -e branch-misses:u ./a.out
┌─Error:───────────────────────────┐ │The perf.data file has no samples!│ │ │ │ │ │Press any key... │ └──────────────────────────────────┘
看樣子還真是系統中斷導致cpu去執行其他任務出現的branch-misses, 值得高興的我合入的spe功能通過抓取的數據驗證,也是正確的,值得慶祝下。