亲测体验Go语言PGO

本文是对官方 Profile-guided optimization in Go 1.21[1] 的学习与实践.


对于PGO的思路,之前就有过类似的想法,有些许差异. 但本质都是通过对以往运行情况的"学习",优化以后程序的运行(有点以史为鉴鉴于往事,资于治道的感觉)

alt

过程很简单:

  1. 收集程序运行过程中的数据。

  2. 编译器根据收集到的数据来分析程序行为,进而做出针对性的性能优化


Profile-guided optimization (PGO). 通过分析Profile来提高程序运行时性能,也称为 profile-directed feedback(PDF)feedback-directed optimization(FDO), 是一项通用的优化技术,在其他语言/软件产品如Chrome中也有使用


亲测体验


项目初始化


alt

以下代码来自官方示例[2]

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语言编写的简单的Web服务器,提供了一个/render的HTTP接口,用于将输入的Markdown文本转换为HTML并返回给客户端。

代码中的import语句导入了一些需要使用的包,包括bytesiolognet/http等。其中net/http/pprof包是用于性能分析。gitlab.com/golang-commonmark/markdown是一个第三方Markdown解析库。

render函数是一个HTTP请求处理函数,它接收POST请求并从请求的主体中读取Markdown文本。然后使用markdown包将Markdown文本转换为HTML,并将结果写入响应的主体中,最后通过HTTP响应返回给客户端。

main函数是程序的入口点。它注册了render函数来处理/render路径的请求,并启动一个HTTP服务器监听端口8080。一旦服务器启动,它将打印一条日志消息,并通过http.ListenAndServe函数来接收和处理传入的HTTP请求。

整体上,这段代码实现了一个简单的Markdown转换服务,通过HTTP接口接收Markdown文本并返回转换后的HTML结果。你可以将这段代码编译并运行,然后通过发送POST请求到http://localhost:8080/render来测试它。


go build -o markdown-nopgo 编译如上代码

./markdown-nopgo 执行

另起一个终端窗口,找一个markdown格式的文档,此处以 Go 项目中的 README.md为例, 获取该README.md: curl -o README.md -L "https://raw.githubusercontent.com/golang/go/c16c2c49e2fa98ae551fc6335215fadd62d33542/README.md"

请求接口: curl --data-binary @README.md http://localhost:8080/render

alt

模拟线上请求,获取profile文件


Go官方这篇博客的作者,写了一个简单的程序[3],来模拟线上的真实负载情况

可以通过执行 go run github.com/prattmic/markdown-pgo/load@latest mock线上的真实请求

同时因为已经导入了 _ "net/http/pprof", 故而可以通过 curl -o cpu.pprof "http://localhost:8080/debug/pprof/profile?seconds=30" 获得profile

alt

得到profile文件后,可以停止两个程序


使用profile文件


当 Go 工具链在主包目录中找到名为 default.pgo 的配置文件时,它将自动启用 PGO。或者, go build 的 -pgo 标志采用用于 PGO 的配置文件的路径

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

这样就有了两个二进制文件, markdown-nopgomarkdown.withpgo


可以通过 go version -m markdown.withpgo 检查构建过程中是否启用了 PGO

alt

性能对比


运行未经过pgo优化的二进制程序 ./markdown-nopgo, 然后执行go test github.com/prattmic/markdown-pgo/load -bench=. -count=40 -source $(pwd)/README.md > nopgo.txt, 保存其benchmark结果

运行经过pgo优化的二进制程序./markdown.withpgo,同样执行go test github.com/prattmic/markdown-pgo/load -bench=. -count=40 -source $(pwd)/README.md > withpgo.txt


最后通过 benchstat nopgo.txt withpgo.txt对比结果

(如果没有安装benchstat,可通过go install golang.org/x/perf/cmd/benchstat@latest安装)

alt

尴尬...经过pgo优化,反而性能下降了~

-count=40 改为 -count=100,再次分别执行两个二进制&进行benchmark,之后对比结果

alt

在n=100情况下,有4%的提升..


相关原理(Under the hood)


详细过程参考官方原文的differential profiling(差异分析) --- 即 在程序运行时获取了优化前和优化后的cpu及heap(主要看总分配计数,即 go tool pprof -sample_index=alloc_objects)相关的pprof文件,然后通过 go tool pprof -diff_base cpu.nopgo.pprof cpu.withpgo.pprof 进行对比

内联优化

能够发现 垃圾回收和内存分配的成本得到了降低,原因是 总体分配的数量相比不启用PGO构建优化前更少

其中 mdurl.Parse(该项目中的一个func)的内存分配次数从之前的近500万次减少为0

这是因为 在非 PGO 构建中, mdurl.Parse 被认为太大,不适合内联。然而,因为我们的 PGO profile文件表明对此函数的调用很热,所以编译器确实内联了它们。

比较cpu.nopgo.pprof和cpu.withpgo.pprof能看到mdurl.Parse被内联优化了

alt

alt
常量传播 是什么?有何作用?

常量传播(Constant Propagation)是编译器优化技术中的一种方法,它涉及在编译时替换那些值已知且不变的变量引用,用它们的实际值代替。这个过程可以减少程序运行时的计算量,提高程序执行的效率。

作用

  1. 提高性能:通过在编译时替换常量,减少运行时的计算,从而提升程序运行速度。
  2. 减少代码体积:有时候,常量传播可以帮助消除一些不必要的代码,从而减少最终程序的大小。
  3. 代码优化:作为编译器优化的一部分,它帮助生成更高效、更紧凑的代码。

实际的编译过程中,常量传播可能涉及更复杂的分析和替换,特别是在大型程序和复杂的代码结构中。这种优化有助于提高程序的执行效率,尤其是在涉及大量计算和逻辑判断的情况下。


去虚拟化


alt

在编程语言优化中,“去虚拟化”(Devirtualization)是一种优化技术,通常用于面向对象编程语言中。它的目的是提高程序的运行效率。为了理解去虚拟化,首先需要了解面向对象编程中的“虚拟函数”(或“虚拟方法”)的概念。

在面向对象的编程语言中,例如C++、Java或C#,虚拟函数是一种可以在派生类中被重写的成员函数。当通过基类的指针或引用调用这样的函数时,会发生动态绑定(或晚期绑定),即运行时根据对象的实际类型来决定调用哪个函数版本。这种机制支持多态,但也带来了性能成本,因为每次调用都需要通过虚拟表(v-table)来确定要执行的正确函数。

去虚拟化是一种编译器优化技术,旨在减少或消除这种运行时开销。如果编译器能够在编译时确定一个特定的虚拟函数调用实际上会调用哪个函数版本,那么它可以直接生成对该特定函数版本的调用,而无需通过虚拟表。这样可以减少间接调用,提高程序运行的效率。

去虚拟化的成功取决于编译器能够多大程度上分析和确定对象的实际类型。在某些情况下,例如当对象的类型在编译时是已知的,去虚拟化可以非常有效。然而,在其他情况下,特别是在涉及复杂继承和多态性的情况下,去虚拟化可能不那么容易实现。




更多参考:

PGO: 为你的Go程序提效5%[4]

Profile Guided Optimizations in Go[5]

Go1.20 那些事:PGO、编译速度、错误处理等新特性,你知道多少?[6]

Profile-guided optimization[7]

PGO 是啥,咋就让 Go 更快更猛了?[8]

探究 Go Profile-Guided Optimizations(PGO)[9]

一文读懂Go 1.20引入的PGO性能优化[10]

参考资料

[1]

Profile-guided optimization in Go 1.21: https://go.dev/blog/pgo

[2]

官方示例: https://go.dev/blog/pgo

[3]

简单的程序: https://github.com/prattmic/markdown-pgo

[4]

PGO: 为你的Go程序提效5%: https://colobu.com/2023/09/13/pgo/

[5]

Profile Guided Optimizations in Go: https://landontclipp.github.io/blog/2023/08/25/profile-guided-optimizations-in-go/

[6]

Go1.20 那些事:PGO、编译速度、错误处理等新特性,你知道多少?: https://blog.csdn.net/EDDYCJY/article/details/128910616

[7]

Profile-guided optimization: https://go.dev/doc/pgo

[8]

PGO 是啥,咋就让 Go 更快更猛了?: https://juejin.cn/post/7168692708725227556

[9]

探究 Go Profile-Guided Optimizations(PGO): https://blog.csdn.net/RA681t58CJxsgCkJ31/article/details/127178724

[10]

一文读懂Go 1.20引入的PGO性能优化: https://zhuanlan.zhihu.com/p/609529412

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/219225.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

js选中起始时间使用标准时间毫秒值计算一年后的当前少一天的日期(并考虑闰年)

js选中起始时间使用标准时间毫秒值计算一年后的当前少一天的日期 实际代码里面带入默认日期’20230301’这个特殊日期&#xff0c;因为下一年的当前日期少一天为闰年的2月会有29天&#xff0c;使用特殊值校验代码效果图 HTML部分代码 <el-button click"chengTime()&q…

微信聊天窗口测试用例

以前没测过客户端的测试&#xff0c;昨天面试被问到聊天窗口测试场景设计&#xff0c;感觉自己答的不好&#xff0c;结束后上网查了一下客户端/app测试的要点&#xff0c;按照测试策略来分&#xff0c;主要涉及到如下测试类型&#xff1a; 1、功能测试 2、性能测试 3、界面测试…

第73讲:深入理解MySQL数据库InnoDB存储引擎:内存结构、磁盘结构与后台线程全面解析

文章目录 1.InnoDB存储引擎的架构2.InnoDB存储引擎的内存结构2.1.Buffer Pool缓冲池2.2.Change Buffer更改缓冲区2.3.自适应Hash索引2.4.Log Buffer日志缓冲区 3.InnoDB存储引擎的磁盘结构3.1.System Tablespace系统表空间3.2.File-Per-Table Tablespaces每个表都有单独的表空间…

ROS话题通信基本操作(C++)

目录 一、发布 1、实现步骤 2、代码实例 二、接收 1、实现步骤 2、代码实例 三、配置运行 1、修改CMakeLists.txt 2、运行结果 一、发布 1、实现步骤 1.包含头文件 2.初始化 ROS 节点:命名(唯一) 3.实例化 ROS 句柄 4.实例化 发布者 对象 5.组织被发布的数据&#…

大学里面转专业介绍

目录 个人情况转专业过程中的经验分享转专业后的学习建议和心态调整转专业后的时间平衡 个人情况 信息科学与工程学院计算机科学与技术专业2019级本科生&#xff0c;曾从物理与微电子科学学院后转入信息科学与技术学院。学习成绩连续三年专业前10% 项目&#xff1a;爬虫项目、…

AIGC+医疗专题:生成式人工智能于医疗健康

今天分享的是AI系列深度研究报告&#xff1a;《AIGC医疗专题&#xff1a;生成式人工智能于医疗健康》。 &#xff08;报告出品方&#xff1a;AREFACT&#xff09; 报告共计&#xff1a;23页 医疗保健中生成性人工智能的崛起: 在承诺与控制之间导航 Generative Al已经历了大规…

【开源】基于Vue.js的实验室耗材管理系统

文末获取源码&#xff0c;项目编号&#xff1a; S 081 。 \color{red}{文末获取源码&#xff0c;项目编号&#xff1a;S081。} 文末获取源码&#xff0c;项目编号&#xff1a;S081。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 耗材档案模块2.2 耗材入库模块2.3 耗…

NowCoder | KY11 二叉树遍历

NowCoder | KY11 二叉树遍历 OJ链接 简单来说就是构建这个二叉树定义结构体通过递归方式根据输入的字符串构建二叉树。对于输入字符串中的每个字符&#xff0c;如果是 ‘#’ 表示空节点&#xff0c;否则创建一个新节点&#xff0c;并递归地构建左右子树。 #include <limit…

五个轻量级免费 PDF 阅读器

便携式文档格式 (PDF) 是商业中最常用的文档类型之一。它们不仅是创建精心设计的文档的更通用的解决方案&#xff0c;而且还具有交互性和安全性。因此&#xff0c;对于寻求具有专业外观的文档的企业来说&#xff0c;PDF 是理所当然的选择。 当谈到查看这些文档时&#xff0c;大…

BUU SQL COURSE 1

四 发现有登录框&#xff0c;爆破半天也爆破不出来&#xff0c;只能从别的地方下手了 F12一下 发现了一个传参 进去发现id可以传参&#xff0c;sql注入一下试试 前三个都有回显&#xff0c;当id4的时候页面没有回显了&#xff0c;正好验证 了页面 有三个新闻 当order by 3的时…

交通强国添力量 无人机巡航为何备受期待?

在高速建设交通强国的过程中&#xff0c;交通运输部海事局计划完善“陆海空天”一体化水上交通运输安全保障体系。无人机巡航系统将在提升海事船舶监管和水上搜救能力方面发挥关键作用&#xff0c;以构建更为全面的监管体系。尽管已初步建立了海事监管体系&#xff0c;但仍存在…

大佬齐聚首钢园,会碰撞出什么火花-百度APOLLO线下沙龙

陈老老老板&#x1f9d9;‍♂️ &#x1f46e;‍♂️本文专栏&#xff1a;生活&#xff08;主要讲一下自己生活相关的内容&#xff09; &#x1f934;本文简述&#xff1a;生活就像海洋,只有意志坚强的人,才能到达彼岸 &#x1f473;‍♂️上一篇文章&#xff1a; 年度总结-你觉…

接口测试详解,一篇足矣。。

本文主要分为两个部分&#xff1a; 第一部分&#xff1a;主要从问题出发&#xff0c;引入接口测试的相关内容并与前端测试进行简单对比&#xff0c;总结两者之前的区别与联系。但该部分只交代了怎么做和如何做&#xff1f;并没有解释为什么要做&#xff1f; 第二部分&#xf…

Java画爱心

Java画爱心代码&#xff0c;每个人都可以被需要 效果图 源代码 package com.example.test; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; import javax.swing.JFrame; class Cardioid extend…

Echarts大屏可视化_02 球体模块制作

继续跟着b站大佬pink老师学大屏可视化 球体模块制作 1.球体模块布局 HTML <div class"column"><div class"no"><div class"no-hd"><ul><li>125811</li><li>104563</li></ul></div&g…

Node-red在Windows上离线部署

Node-red在Windows上离线部署 前言 在实际项目的使用过程中&#xff0c;生产环境的服务器很多情况下是不允许使用外部网络的&#xff0c;因此&#xff0c;基于npm直接安装的模式&#xff0c;在很多情况下不适用&#xff0c;需要考虑如何将Node-red进行离线安装&#xff1b; …

RH850P1X芯片学习笔记-Overview

文章目录 Outline产品列表功能框图特点Pin和引脚功能CPU系统CPUFPU浮点运算单元中断处理保护机制指令缓存Local RAMGlobal RAM处理器间通信和相互排斥机制 操作模式中断功能DMA电源供电Reset控制单元时钟控制单元CSIH-SPIMCAN看门狗计时器系统计时器GTM通用定时器模块外设互联P…

使用python+poco+夜神模拟器进行自动化测试实例

网易最近出的一款自动化UI测试工具&#xff1a;Airtest 挺火的&#xff0c;还受到谷歌的推荐。我试着用了一下&#xff0c;感觉优缺点还是蛮明显的。对初学者来说&#xff0c;能用到的也就是图像识别的功能&#xff0c;这块做得比老牌的按键精灵弱很多。不过Airtest集合了poco框…

windows10系统下替换、修改jar中的文件并重新打包成jar文件然后运行

目录 1、jar文件简述2、问题来源3、操作步骤3.1 解压jar包3.2 替换或者更改操作3.3 重新打成jar包3.4 确认是否修改成功3.5 运行程序 附录&#xff1a;常见命令参数 1、jar文件简述 JAR 文件就是 Java Archive &#xff08; Java 档案文件&#xff09;&#xff0c;它是 Java 的…

四川柩震栩电子商务有限公司可靠吗?

作为当今互联网时代的领军者&#xff0c;抖音已经成为了一个家喻户晓的平台。而在这个平台上&#xff0c;一家名为四川柩震栩电子商务有限公司的企业也崭露头角&#xff0c;他们的电商服务正改变着整个电商行业的格局。 四川柩震栩电子商务有限公司是一家充满活力和创新精神的企…