当编译器优化程序时,不得不依赖猜测:哪些函数值得内联?分支的常见方向是哪边?接口调用实际指向哪个方法?编译期无法知晓,只能依靠启发式规则。而Profile-guided optimization(PGO)用测量代替猜测:先运行程序,记录时间都花在哪里,再将该记录反馈给编译器进行二次构建。 PGO早已是编译器系统的常见特性。Google在2016年将其应用于Windows版Chrome,报告最高收益达15%。目前主流Web浏览器几乎都采用PGO构建。如今还有比PGO配合启发式更先进的技术,比如用AI识别调用模式,但这些并不总在通用编译器中可用。 Go从1.20版本起支持PGO。你只需收集profile,然后传给编译器即可。 所谓CPU profile是对程序时间消耗的统计记录。程序运行时,Go运行时系统约每秒中断程序一百次,并记下当时的调用栈。几秒钟后,就能得到数千个样本,统计这些样本便能知道哪些函数在执行、由谁调用。在Go中,可用如下方式生成profile: f, _ := os.Create("cpu.pprof") pprof.StartCPUProfile(f) // from runtime/pprof defer pprof.StopCPUProfile() 编译器读取调用栈计数,主要用于两件事:内联频繁被调用的热调用点,以及将“目标几乎总是同一具体类型”的接口调用去虚拟化。 为验证效果,我选用三个JSON文档进行解析——twitter.json、canada.json、citm_catalog.json,并用标准库encoding/json反序列化为interface{}。无profile的基线速度分别为:twitter.json 112 MB/s,canada.json 74 MB/s,citm_catalog.json 116 MB/s。 整个流程只需三条命令: go build -o bench . # 普通构建 ./bench -profile cpu.pp go build -pgo=auto -o bench-pgo . # 使用profile构建 PGO的价值在于让编译器面向真实运行场景优化,而非靠猜测。对于负载明确或长期稳定的服务,这一技术能带来显著性能回报。Go 1.20及以后版本已让这项技术触手可及,你的下一个项目不妨试试。
热门跟贴