Go1.20で入ったPGO(Profile-guided Optimization Preview)を試してみた

Table of Contents

概要

PGOはコンパイラ最適化手法の1つ。実世界で利用された統計データをもとに、最適化を実施する手法。RustChromeでは既に取り入れられている。

参考リンク

Go Blogにしたがって試す

  • 手元で動かしたコードは https://github.com/task4233/go-pgo-test
  • Goコンパイラは最適化してバイナリを作成する
  • 最適化の手法
  • Goはリリースごとに最適化を改善するが非常に困難
    • 過度な最適化はパフォーマンスの低下やコンパイル時間の増加に繋がる
      • 全ての関数を最大限まで最適化することは難しい
    • コンパイラはcommonパスとuncommonパスを判断する必要があり、静的ヒューリスティックに基づいて推測を行う必要がある
  • コンパイラは、コードがどのように動作するか把握できない
    • プロファイラによって本番環境の動作を評価できる
    • 最も頻繁に利用される関数をより積極的に最適化することができる
  • コンパイラ最適化のために、アプリケーション動作のプロファイルを使用することでこの問題に対処する
    • これをPGO(Profile-Guided Optimazation)、もしくはFDO(Feedback-Directed Optimization)と呼ぶ
  • サンプルプログラム

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 run example.com/markdown/load &
curl -o cpu.pprof "http://localhost:8080/debug/pprof/profile?seconds=30"

  • PGOの利用
    • -pgo フラッグをbuild時に設定すれば良い
    • Go1.20では -pgo=off がデフォルト設定
    • 将来的には -pgo=auto がデフォルト設定になる
    • デフォルトでは、mainパッケージのあるディレクトリの default.pgo が利用される
  • benchstatを利用した比較

make bench
goos: darwin
goarch: arm64
pkg: github.com/task4233/pgo-test/load
       │ ./log/nopgo.txt │         ./log/withpgo.txt          │
       │     sec/op      │   sec/op     vs base               │
Load-8       85.37µ ± 1%   83.36µ ± 2%  -2.35% (p=0.001 n=20)
Posted on Feb 15, 2023