Go PGO 快速上手,性能可提高 2~4%!

简介: Go PGO 快速上手,性能可提高 2~4%!

温习一下 PGO


Profile-guided optimization (PGO),PGO 是计算机编程中的一种编译器优化技术,借助配置文件来引导编译,达到提高程序运行时性能的目的

863384db97b194741846f5805692597d.png

翻译过来是使用配置文件引导的优化,能提供应用程序的性能。也被称为:


  • profile-directed feedback(PDF)
  • feedback-directed optimization(FDO)


该项优化是一个通用技术,不局限于某一门语言。像是我们常见很多应用都有所使用其来优化。如下几个案例:

  • Chrome 浏览器,在 64 位版本的 Chrome 中从 53 版开始启用 PGO, 32 位版在 54 版中启用。
  • AutoFDO 进行了 PGO 的优化,直接将某数据中心中的 C/C++ 程序的性能提高了 5-15%(不用改业务代码)。


Go 怎么读取 PGO


PGO 第一个版本先支持的 pprof CPU,直接读取 pprof CPU profile 文件来完成优化。

有以下两种方式:


  • 手动指定:Go 工具链在 go build 子命令增加了-pgo=<path>参数,用于显式指定用于 PGO 构建的 profile 文件位置。
  • 自动指定:当 Go 工具链在主模块目录下找到 default.pgo 的配置文件时,将会自动启用 PGO。


快速 Demo


初始化应用程序


首先我们创建一个 Demo 目录,用于做一系列的实验。执行如下命令:


$ mkdir pgo-demo && cd pgo-demo


初始化模块路径和拉取程序所需的依赖:


$ go mod init example.com/markdown
go: creating new go.mod: module 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
 }
 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/10/02 13:55:40 Serving on port 8080...


运行起来后进行验证,这是一个将 Markdown 转换为 HTML 的应用程序。


我们从 GitHub 上拉取一份 markdown 文件并给到该程序进行转换。如下命令:

$ 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>
...


如果正常则说明运行没问题。


收集 PGO 所需的配置文件


一般情况下,我们可以通过生产、测试环境的 pprof 采集所需的 profile 文件,用于做 PGO 的配置文件。

但由于示例没有对应的生产环境。本次快速 Demo,Go 官方提供了一个简单的程序来快速的发压。


在确保前面小节的 pgo-demo 程序正常运行的情况下。运行如下命令,启动一个发压程序:

$ go run github.com/prattmic/markdown-pgo/load@latest


收集对应的 profile 文件:


$ curl -o cpu.pprof "http://localhost:8080/debug/pprof/profile?seconds=30"


生成了一个 cpu.pprof 文件,可以在后续使用。


应用程序使用 PGO


前面我们有提到,当模块目录下包含 default.pgo 时。Go 工具链就会自动应用 PGO


我们只需要将前面的 cpu.pprof 修改一下即可。执行如下命令:


$ mv cpu.pprof default.pgo
$ go build -o markdown.withpgo


编译成功后,使用如下命令验证是否正常:

$ go version -m markdown.withpgo
markdown.withpgo: go1.21.1
 path example.com/markdown
 mod example.com/markdown (devel) 
 ...
 build GOOS=darwin
 build GOAMD64=v1
 build -pgo=/Users/eddycjy/app/go/pgo-demo/default.pgo


可以看到最后的build -pgo=...,代表该应用程序成功应用了我们的 default.pgo 文件(启用 PGO)。

总结

PGO 作为 Go 新版本的一个性能好帮手,在官方给出的数据中启用 PGO 后,性能能够得到一定的提升。但也会带来其他方面(CPU、大小等)的开销增加。


如本文的例子中,官方给出的数据是程序性能提升了约 2~4%,CPU 使用率会带来 2~7% 的开销增加。也可能会导致构建时长变长一些、编译后的二进制文件会稍微大一些。


面对一些场景,PGO 是一个不错的性能优化方式。但有利必有弊,就看这个应用程序的类型和综合取舍了。

相关文章
|
1月前
|
Go API 数据库
Go 语言中常用的 ORM 框架,如 GORM、XORM 和 BeeORM,分析了它们的特点、优势及不足,并从功能特性、性能表现、易用性和社区活跃度等方面进行了比较,旨在帮助开发者根据项目需求选择合适的 ORM 框架。
本文介绍了 Go 语言中常用的 ORM 框架,如 GORM、XORM 和 BeeORM,分析了它们的特点、优势及不足,并从功能特性、性能表现、易用性和社区活跃度等方面进行了比较,旨在帮助开发者根据项目需求选择合适的 ORM 框架。
93 4
|
1月前
|
中间件 Go API
Go语言中几种流行的Web框架,如Beego、Gin和Echo,分析了它们的特点、性能及适用场景,并讨论了如何根据项目需求、性能要求、团队经验和社区支持等因素选择最合适的框架
本文概述了Go语言中几种流行的Web框架,如Beego、Gin和Echo,分析了它们的特点、性能及适用场景,并讨论了如何根据项目需求、性能要求、团队经验和社区支持等因素选择最合适的框架。
80 1
|
7月前
|
存储 算法 编译器
掌握Go语言:探索Go语言递归函数的高级奥秘,优化性能、实现并发、解决算法难题(28)
掌握Go语言:探索Go语言递归函数的高级奥秘,优化性能、实现并发、解决算法难题(28)
136 0
|
3月前
|
缓存 安全 Java
如何利用Go语言提升微服务架构的性能
在当今的软件开发中,微服务架构逐渐成为主流选择,它通过将应用程序拆分为多个小服务来提升灵活性和可维护性。然而,如何确保这些微服务高效且稳定地运行是一个关键问题。Go语言,以其高效的并发处理能力和简洁的语法,成为解决这一问题的理想工具。本文将探讨如何通过Go语言优化微服务架构的性能,包括高效的并发编程、内存管理技巧以及如何利用Go生态系统中的工具来提升服务的响应速度和资源利用率。
|
4月前
|
Kubernetes 数据可视化 Java
|
4月前
|
设计模式 Java 编译器
Go - 基于逃逸分析来提升程序性能
Go - 基于逃逸分析来提升程序性能
47 2
|
4月前
|
消息中间件 NoSQL Kafka
go-zero微服务实战系列(九、极致优化秒杀性能)
go-zero微服务实战系列(九、极致优化秒杀性能)
|
5月前
|
JSON Java Go
Go 语言性能优化技巧
在Go语言中优化性能涉及数字字符串转换(如用`strconv.Itoa()`代替`fmt.Sprintf()`)、避免不必要的字符串到字节切片转换、预分配切片容量、使用`strings.Builder`拼接、有效利用并发(`goroutine`和`sync.WaitGroup`)、减少内存分配、对象重用(`sync.Pool`)、无锁编程、I/O缓冲、正则预编译和选择高效的序列化方法。这些策略能显著提升代码执行效率和系统资源利用率。
105 13
|
4月前
|
运维 监控 算法
[go 面试] 优化线上故障排查与性能问题的方法
[go 面试] 优化线上故障排查与性能问题的方法
|
4月前
|
Rust JavaScript Java
简单对比Java、Python、Go、Rust等常见语言计算斐波拉契数的性能
简单对比Java、Python、Go、Rust等常见语言计算斐波拉契数的性能