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

大家好,我是煎鱼。

2023 年初,在 Go1.20,PGO 发布了预览版本。在本次 Go1.21 的新版本发布,修复了各种问题后,PGO 已经正式官宣生产可用。

今天这篇文章就是和大家一起跟着官方示例快速体验一下他的性能优化和使用。

温习一下 PGO

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

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

  • 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 目录,用于做一系列的实验。执行如下命令:

shell 复制代码
$ mkdir pgo-demo && cd pgo-demo

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

vbnet 复制代码
$ 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 文件,写入如下

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 复制代码
$ go build -o markdown.nopgo
$ ./markdown.nopgo 
2023/10/02 13:55:40 Serving on port 8080...

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

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

typescript 复制代码
$ 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 复制代码
$ go run github.com/prattmic/markdown-pgo/load@latest

收集对应的 profile 文件:

ruby 复制代码
$ curl -o cpu.pprof "http://localhost:8080/debug/pprof/profile?seconds=30"

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

应用程序使用 PGO

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

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

go 复制代码
$ mv cpu.pprof default.pgo
$ go build -o markdown.withpgo

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

ini 复制代码
$ 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、大小等)的开销增加。

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

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

文章持续更新,可以微信搜【脑子进煎鱼了】阅读,本文 GitHub github.com/eddycjy/blo... 已收录,学习 Go 语言可以看 Go 学习地图和路线,欢迎 Star 催更。

Go 图书系列

推荐阅读

相关推荐
幼儿园老大*1 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
童先生8 小时前
Go 项目中实现类似 Java Shiro 的权限控制中间件?
开发语言·go
幼儿园老大*10 小时前
走进 Go 语言基础语法
开发语言·后端·学习·golang·go
架构师那点事儿15 小时前
golang 用unsafe 无所畏惧,但使用不得到会panic
架构·go·掘金技术征文
于顾而言1 天前
【笔记】Go Coding In Go Way
后端·go
qq_172805591 天前
GIN 反向代理功能
后端·golang·go
follycat2 天前
2024强网杯Proxy
网络·学习·网络安全·go
OT.Ter2 天前
【力扣打卡系列】单调栈
算法·leetcode·职场和发展·go·单调栈
探索云原生2 天前
GPU 环境搭建指南:如何在裸机、Docker、K8s 等环境中使用 GPU
ai·云原生·kubernetes·go·gpu
OT.Ter2 天前
【力扣打卡系列】移动零(双指针)
算法·leetcode·职场和发展·go