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

大家好,我是煎魚。

2023 年初,在 Go1.20,PGO 發佈了預覽版本。在本次 Go1.21 的新版本發佈,修復了各種問題後,PGO 已經正式官宣生產可用。

今天這篇文章就是和大家一起跟着官方示例,快速體驗他的性能優化和使用。

溫習一下 PGO

Profile-guided optimization (PGO),PGO 是計算機編程中的一種編譯器優化技術,藉助配置文件來引導編譯,達到提高程序運行時性能的目的

翻譯過來是使用配置文件引導的優化,能提供應用程序的性能。也被稱爲:

該項優化是一個通用技術,不侷限於某一門語言。像是我們常見很多應用都有所使用其來優化。如下幾個案例:

Go 怎麼讀取 PGO

PGO 第一個版本先支持的 pprof CPU,直接讀取 pprof CPU profile 文件來完成優化。

有以下兩種方式:

快速 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 是一個不錯的性能優化方式。但有利必有弊,就看這個應用程序的類型和綜合取捨了。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/mjbx3fUMjkynuhXoQ1yxog