在2023年早些时候,Go 1.20发布了供用户测试的概要版本的基于性能分析的优化(PGO)。经过解决预览版已知的限制,并得益于社区反馈和贡献的进一步改进,Go 1.21中的PGO支持已经准备好供一般生产使用!请查阅性能分析优化用户指南以获取完整的文档。
下面,我们将通过一个示例来演示如何使用PGO来提高应用程序的性能。在我们深入讨论之前,什么是“基于性能分析的优化”(Profile-Guided Optimization,PGO)?
当您构建一个Go二进制文件时,Go编译器会执行优化操作,以尽量生成性能最佳的二进制文件。例如,常量传播可以在编译时评估常量表达式,避免运行时的评估成本。逃逸分析避免了局部作用域对象的堆分配,从而避免了垃圾收集的开销。内联操作将简单函数的主体复制到调用者中,通常使调用者进一步优化(如额外的常量传播或更好的逃逸分析)。去虚拟化将对接口值的间接调用转换为对具体方法的直接调用(这通常允许调用的内联)。
Go会在每个版本中改进优化,但这并不是一项容易的任务。一些优化是可调节的,但编译器不能仅仅对每个优化都“加大力度”,因为过于激进的优化实际上可能会降低性能或导致构建时间过长。其他优化需要编译器对函数中的“常见”和“不常见”路径进行判断。编译器必须基于静态启发式算法进行最佳猜测,因为它无法知道哪些情况在运行时将会常见。
但是,有没有可能知道呢?
在没有确切信息的情况下,了解代码在生产环境中的使用方式,编译器只能对包的源代码进行操作。但我们有一种工具来评估生产行为:性能分析。如果我们向编译器提供一个性能分析文件,它就可以做出更明智的决策:更积极地优化最常用的函数,或更准确地选择常见情况。
使用应用程序行为的性能分析文件进行编译器优化被称为“基于性能分析的优化”(Profile-Guided Optimization,PGO)(也称为“反馈导向优化”(Feedback-Directed Optimization,FDO))。
示例
让我们构建一个将Markdown转换为HTML的服务:用户上传Markdown源文件到/render端点,该端点返回HTML转换结果。我们可以使用gitlab.com/golang-commonmark/markdown来轻松实现这个功能。
首先
$ go mod init example.com/markdown
$ go get gitlab.com/golang-commonmark/markdown@bf3e522c626a
main.go文件内容如下:
package main
import (
"bytes"
"io"
"log"
"net/http"
_ "net/http/pprof"
"gitlab.com/golang-commonmark/markdown"
)
func render(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Only POST allowed", http.StatusMethodNotAllowed)
return
}