原文在这里。
原文发布于2023年2月8日
在构建Go二进制文件时,Go编译器会进行优化,以尽可能生成性能最佳的二进制文件。例如,常量传播可以在编译时对常量表达式进行求值,避免了运行时的计算开销;逃逸分析可以避免对局部作用域对象进行堆分配,从而减少了垃圾回收的负担;内联则将简单函数的代码体复制到调用处,通常能够进一步优化调用处的代码(例如额外的常量传播或更好的逃逸分析)。
Go在发布的每个版本中都会改进优化,但这并不总是一项容易的任务。某些优化是可调节的,但编译器不能对每个函数都进行过度激进的优化,因为过于激进的优化实际上可能会损害性能或导致过长的构建时间。其他优化要求编译器对函数中的“常见”和“不常见”路径进行判断。编译器必须根据静态启发式规则进行最佳猜测,因为它无法在运行时知道哪些情况将是常见的。
但现在编译器可以在运行时知道哪些情况是常见的了。
在没有关于代码在生产环境中如何使用的确切信息的情况下,编译器只能对包的源代码进行操作。但是我们确实有一种工具来评估生产行为:性能分析。如果我们向编译器提供一个性能分析文件,它就可以做出更明智的决策:对最常用的函数进行更积极的优化,或更准确地选择常见情况。
使用应用程序行为的性能分析文件进行编译器优化的方法被称为基于性能分析的优化(Profile-Guided Optimization,简称PGO,也被称为反馈导向优化(Feedback-Directed Optimization,简称FDO))。
PGO/FDO通过收集和分析运行时的性能数据,使得编译器能够更准确地了解代码的执行特性,从而进行更精细的优化。通过结合静态分析和动态运行时数据,PGO/FDO可以产生更优化的代码,提高程序的性能和效率。这种技术在提高大型复杂应用程序的性能方面非常有用,特别是对于高度频繁执行的代码路径进行优化。
Go 1.20中包含了PGO的初步支持,作为预览版本提供。请参阅profile-guided optimization user guide以获取完整的文档。尽管距离在生产环境中使用还有一段距离,但仍希望大家在工作中使用,并反馈遇到的问题或意见。
示例
以Markdown转HTML服务为例:用户通过/render
上传Markdown文件,然后接收转换后的HTML文件。这里使用gitlab.com/golang-commonmark/markdown。
创建项目
$ go mod init example.com/markdown
$ go get gitlab.com/golang-commonmark/markdown
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
}
src, err := io.ReadAll(r.Body)
if err != nil {
log.Printf("error reading body: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
md := markdown.New(
markdown.XHTMLOutput(true),
markdown.Typographer(true),
markdown.Linkify(true),
markdown.Tables(true),
)
var buf bytes.Buffer
if err := md.Render(&buf, src); err != nil {
log.Printf("error converting markdown: %v", err)
http.Error(w, "Malformed markdown", http.StatusBadRequest)
return
}
if _, err := io.Copy(w, &buf); err != nil {
log.Printf("error writing response: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/render", render)
log.Printf("Serving on port 8080...")
log.Fatal(http.ListenAndServe(":8080", nil))
}
启动服务:
$ go build -o markdown.nopgo
$ ./markdown.nopgo
2023/06/25 11:27:13 Serving on port 8080...
使用Go项目的README来进行测试:
$ curl -o README.md -L "https://raw.githubusercontent.com/golang/go/c16c2c49e2fa98ae551fc6335215fadd62d33542/README.md"
$ curl --data-binary @README.md http://localhost:8080/render
<h1>The Go Programming Language</h1>
<p>Go is an open source programming language that makes it easy to build simple,
reliable, and efficient software.</p>
...
<p>Note that the Go project uses the issue tracker for bug reports and
proposals only. See <a href="https://go.dev/wiki/Questions">https://go.dev/wiki/Questions</a> for a list of
places to ask questions about the Go language.</p&g