背景
在公司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功能通过抓取的数据验证,也是正确的,值得庆祝下。