原文在这里

原文发布于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>

性能分析

现在我们来采集一个profile文件,再使用PGO来重新构建服务,看看性能能提升多少。

main.go中,我们导入了net/http/pprof包,它会自动为服务器添加一个/debug/pprof/profile地址,用于获取CPU分析数据。

通常情况下,我们都是从生产环境中收集性能分析数据,以便编译器能够获取在实际生产环境中的行为情况。但这个示例没有一个真实的“生产”环境,我们将创建一个简单的程序来生成负载,同时收集性能分析数据。将该程序的源码复制到load/main.go,并启动负载生成器(确保服务器仍在运行!)。

$ go run example.com/markdown/load

下载性能分析文件:

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

下载完成后,关闭服务。

启用PGO

我们可以使用go build命令的-pgo标志要求Go工具链使用PGO进行构建。-pgo标志可以接受以下两种参数:

  • 指定要使用的性能分析文件的路径
  • 使用"auto",它将使用主包目录中的default.pgo文件

我们建议将default.pgo性能分析文件提交到你的代码仓库中。将性能分析文件与源代码放在一起,可以确保用户只需获取代码仓库(无论是通过版本控制系统还是通过go get命令),就能自动获得性能分析文件,并且构建过程仍然可重现。在Go 1.20中,默认的-pgo选项是off,因此用户仍需要添加-pgo=auto选项,但预计将来的Go版本将把默认值改为-pgo=auto,这样任何构建该二进制文件的人都将获得PGO的好处。

构建:

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

性能对比

我们将使用一个基于Go的基准测试版本的负载生成器来评估PGO对性能的影响。将这个基准测试的代码复制到load/bench_test.go文件中。

首先没有使用PGO的情况下进行测试:

$ ./markdown.nopgo

进行测试:

$ go test example.com/markdown/load -bench=. -count=100 -source ../README.md > nopgo.txt

然后启用PGO:

$ ./markdown.withpgo

进行测试:

$ go test example.com/markdown/load -bench=. -count=100 -source ../README.md > withpgo.txt

运行结束后进行结果对比:

$ go install golang.org/x/perf/cmd/benchstat@latest
$ benchstat nopgo.txt withpgo.txt
goos: linux
goarch: amd64
pkg: example.com/markdown/load
cpu: Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz
│ nopgo.txt │ withpgo.txt │
│ sec/op │ sec/op vs base │
Load-8 445.1µ ± 4% 408.6µ ± 2% -8.21% (p=0.000 n=100)

新版本大约快了8.2%!在Go 1.20中,通过启用PGO,可以获得2%到4%的CPU使用率提升。性能分析文件包含了关于应用程序行为的丰富信息,而Go 1.20仅仅开始利用这些信息进行内联优化。未来的发布版本将继续改进性能,因为编译器的更多部分将利用PGO带来的好处。

原文中效率提升了2.6%

文中的代码可以在这里找到。


声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处。

Author: mengbin

blog: mengbin

Github: mengbin92

cnblogs: 恋水无意


PGO前瞻的更多相关文章

  1. JavaScript:正则表达式 前瞻

    正向前瞻:用来捕获出现在特定字符之前的字符,只有当字符后面跟着某个特定字符才去捕获它.(?=) 负向前瞻:它用匹配只有当字符后面不跟着某个特定字符时才去匹配它.(?!) 在执行前瞻和负向前瞻之类的运算 ...

  2. msvc2010生成的指令序列有问题,可能跟pgo有关

    正常序列 有问题序列 这段代码程序启动是执行,会导致崩溃 工程使用ltcg pgo,也就是说,第一次编译连接完成后,会跑一次profile,再执行连接器代码生成优化. 构建记录显示,ltcg已跑完,说 ...

  3. R0:前瞻

    原文链接http://www.wangafu.net/~nickm/libevent-book/Ref0_meta.html Libevent使用手册:前瞻 总览: Libevent是一个用来写高性能 ...

  4. cloudstack4.4新增功能前瞻

    cloudstack4.4.0新功能前瞻 转载请注明地址:http://blog.csdn.net/zt689/article/details/37698989 1.   cloudstack4.4. ...

  5. 2016 SyScan360 国际前瞻信息安全会议 多角度探讨信息安全

    SyScan360国际前瞻信息安全会议由与中国第一大互联网安全公司-360公司与SyScan前瞻信息安全技术年会(TheSymposiumonSecurityforAsiaNetwork,以下简称Sy ...

  6. JS 正则表达式否定匹配(正向前瞻)

    引言:JS 正则表达式是 JS 学习过程中的一大难点,繁杂的匹配模式足以让人头大,不过其复杂性和其学习难度也赋予了它强大的功能.文章从 JS 正则表达式的正向前瞻说起,实现否定匹配的案例.本文适合有一 ...

  7. JUnit5 技术前瞻

    更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢! 原文链接:http://www.cnblogs.com/zishi/p/6868495.html   JUnit ...

  8. 护航者,腾讯云: 2017年度游戏行业DDoS态势报告—回溯与前瞻

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 作者:腾讯游戏云 前言 自14年开始,全球DDoS攻击持续爆发,攻击峰值不断创记录.2017年,这种依靠超大流量不断冲击服务器和带宽造成业务 ...

  9. 单元测试系列之六:JUnit5 技术前瞻

    更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢! 原文链接:http://www.cnblogs.com/zishi/p/6868495.html   JUnit ...

  10. 2019年Python数据挖掘就业前景前瞻

    Python语言的崛起让大家对web.爬虫.数据分析.数据挖掘等十分感兴趣.数据挖掘就业前景怎么样?关于这个问题的回答,大家首先要知道什么是数据挖掘.所谓数据挖掘就是指从数据库的大量数据中揭示出隐含的 ...

随机推荐

  1. jQuery模糊匹配checkbox全选 value实现checkbox部分或全部全选

    本文章总结jQuery实现checkbox三种情况的全选功能 第一种:等值全选,也称name的等值全选,通过checkbox的名称name实现. 第二种:模糊全选,也称id模糊全选,通过checkbo ...

  2. 火山引擎DataTester:小改动带来大收益,A/B实验助力幸福里APP精准优化

      幸福里APP是抖音集团旗下开发运营的集内容.社区.工具于一体的房产信息综合平台,基于个性化推荐引擎向用户推荐优质的房产内容房源信息.本文将介绍幸福里APP应用火山引擎A/B测试DataTester ...

  3. Axure 标记元件

    快照:可以用来表示控件的截图功能 箭头:有了连线,基本很少用它 便签:相关于便利贴,写些说明.备注, 标记:标记好数字,对应数字的标记做解释说明

  4. Jenkins Pipeline 流水线 - 上传文件 Publish over SSH 执行命令

    Jenkins Pipeline 流水线 - 上传文件 Publish over SSH 执行命令 Jenkins插件安装 Publish over SSH 系统配置 生成代码 sshPublishe ...

  5. django DRF

    博客目录 web应用模式 api接口 接口测试工具postman restful规范 drf安装 序列化和反序列化 CBV源码分析 drf之APIView分析 drf之Request对象分析 drf- ...

  6. three.js 消防模拟火焰烟雾效果

    ParticleEngine.js实现烟雾效果 参考网址:http://stemkoski.github.io/Three.js/Particle-Engine.html ParticleEngine ...

  7. Java 设计模式课堂作业记录

    第二章 P25,有人将面向对象设计原则简单归类为 3 条:①封装变化点: ②对接口进行编程: ③多使用组合,而不是继承.请查阅相关资料谈谈理解 3.7 : 该三大原则 应该算 是OO的基础,很多OO设 ...

  8. 20级训练赛Round #4

    给20级学弟学妹们带带榜单 A - 凯少的动作序列 枚举情况替换即可 #include <bits/stdc++.h> using namespace std; using ll = lo ...

  9. 如何在 Debian 12 上安装 MariaDB

    MariaDB 是一个开源多线程的关系数据库管理系统,是 MySQL 的替代品. MariaDB 是 Debian 中 MySQL 的默认替换方案. 本教程介绍如何在 Debian 12 上安装 Ma ...

  10. 数字孪生 3D 风电场,智慧风电之陆上风电

    前言 "十四五"期间,在传统产业数字化升级和绿色改造领域.绿色低碳城镇化和现代城市建设领域.绿色低碳消费领域,和可再生能源或电力系统建设等领域,总投资可以达到近 45 万亿,平均每 ...