Go应用性能分析实战

news2025/1/19 20:32:19

Go很适合用来开发高性能网络应用,但仍然需要借助有效的工具进行性能分析,优化代码逻辑。本文介绍了如何通过go test benchmark和pprof进行性能分析,从而实现最优的代码效能。原文: Profiling Go Applications in the Right Way with Examples[1]

Go Gopher为"A Journey With Go"创作的插图,作者Renee French
Go Gopher为"A Journey With Go"创作的插图,作者Renee French
什么是性能分析?

性能分析(Profiling) 是分析应用程序从而识别阻碍应用性能的瓶颈的基本技术,有助于检测代码的哪些部分执行时间太长消耗太多资源(如CPU和内存)。

分析方法

有三种分析方法。

  • Go test(包含基准测试)
  • 基于 runtime/pprof [2]运行时分析
  • 基于 net/http/pprof [3]Web分析
分析类型
  • CPU (收集应用程序CPU使用情况的数据)
  • 堆(Heap)/内存(Memory) (收集应用程序内存使用情况的数据)
  • Goroutine (识别创建最多Goroutine的函数)
  • 阻塞 (识别阻塞最多的函数)
  • 线程 (识别创建线程最多的函数)
  • 互斥锁 (识别有最多锁竞争[4]的函数)

本文将主要关注使用上述方法进行CPU和内存分析。

1. 基准测试(Benchmarking)

我想实现著名的两数之和算法[5],这里不关注实现细节,直接运行:

go test -bench=.

-bench参数运行项目中的所有基准测试。

go test bench输出
go test bench输出

根据上面的输出,与其他方法相比,TwoSumWithBruteForce是最有效的方法。别忘了结果取决于函数输入,如果输入一个大数组,会得到不同的结果。😅

如果输入go help testflag,将看到许多参数及其解释,比如countbenchtime等,后面将解释最常用的参数。

  • 如果要运行特定函数,可以通过如下方式指定:
go test -bench='BenchmarkTwoSumWithBruteForce'
  • 默认情况下,基准测试函数只运行一次。如果要自定义,可以使用 count参数。例如,
go test -bench='.' -count=2

输出如下所示。

带count参数的基准测试输出
带count参数的基准测试输出
  • 默认情况下,Go决定每个基准测试操作的运行时间,可以通过自定义 benchtime='2s'指定。

可以同时使用countbenchtime参数,以便更好的度量基准函数。请参考How to write benchmarks in Go[6]

示例代码请参考Github[7]

在现实世界中,函数可能既复杂又长,计时毫无作用,因此需要提取CPU和内存分析文件以进行进一步分析。可以输入

go test -bench='.' -cpuprofile='cpu.prof' -memprofile='mem.prof'

然后通过pprof[8]工具对其进行分析。

1.1 CPU分析

如果输入

go tool pprof cpu.prof

并回车,就会看到pprof交互式控制台。

go tool pprof cpu.prof输出
go tool pprof cpu.prof输出

我们来看看最主要的内容。

  • 输入 top15查看执行期间排名前15的资源密集型函数。 (15表示显示的节点数。)
alt

为了解释清楚,假设有一个A函数。

func A() {
   B()             // 耗时1s
   DO STH DIRECTLY // 耗时4s
   C()             // 耗时6s
}

flat值和cum值计算为: flat值为A=4, cum值为A=11(1s + 4s + 6s)

  • 如果要基于cum进行排序,可以键入 top15 -cum。也可以分别使用 sort=cumtop15命令。
  • 如果通过 top获得更详细的输出,可以指定 granularity选项。例如,如果设置 granularity=lines,将显示函数的行。
粒度为行的top15输出
粒度为行的top15输出

得益于此,我们可以识别导致性能问题的函数的特定行。😌

  • 输出还显示了运行时函数和用户自定义函数。如果只想关注自己的函数,可以设置 hide=runtime并再次执行 top15
带hide选项的top15
带hide选项的top15

可以通过输入hide=来重置。

  • 此外,可以使用 show命令。例如,输入 show=TwoSum
alt
  • 如果只关注指定节点,可以使用 focus命令。例如关注 TwoSumOnePassHashTable,显示为
同时带hide和focus的top输出
同时带hide和focus的top输出

可以输入focus=来重置。

  • 如果需要获取该功能的详细信息,可以使用 list命令。例如,想获得关于 TwoSumWithTwoPassHashTable函数的详细信息,输入 list TwoSumWithTwoPassHashTable
list TwoSumWithTwoPassHashTable输出
list TwoSumWithTwoPassHashTable输出
  • 如果要查看图形化的调用栈,可以键入 web
web输出
web输出
node细节
node细节

后面将提供更多关于分析图表的细节。

  • 还可以键入 gifpdf以与他人共享相应格式的分析数据。😃
1.2 内存分析

如果输入go tool pprof mem.prof并回车

go tool pprof mem.prof输出
go tool pprof mem.prof输出
top10输出
top10输出

注意,上面提到的flat和cum是相同的东西,只是测量不同的东西(CPU单位ms,内存单位MB)。

  • list命令
list TwoSumWithBruteForce输出
list TwoSumWithBruteForce输出
  • web命令
web命令输出
web命令输出

可以使用CPU分析部分中提到的所有命令。

下面看一下另一个方法,runtime/pprof。🚀

2. 基于runtime/pprof[9]的运行时分析

基准测试对单个函数的性能很有用,但不足以理解整体情况,这时就需要用到runtime/pprof💠。

2.1 CPU分析

基准测试内置CPU和内存分析,但如果需要让应用程序支持运行时CPU分析,必须首先显示启用。

alt

如果执行go run .,将看到生成的cpu.prof文件,可以通过基准测试部分提到的go tool pprof cpu.prof对齐进行分析。

本节将介绍我最喜欢的特性之一pprof.Labels此特性仅适用于CPU和goroutine分析[10]

如果要向特定函数添加一个或多个标签,可以使用pprof.Do函数。

pprof.Do(ctx, pprof.Labels("label-key""label-value"), func(ctx context.Context) {
    // 执行标签代码
})

例如,

向特定函数添加标签
向特定函数添加标签

在pprof交互式控制台中,键入tags,将显示带了有用信息的标记函数。

tags输出
tags输出

可以用标签做很多事情[11],阅读Profiler labels in Go[12]可以获得更多信息。

pprof还有很棒的web界面,允许我们使用各种可视化方式分析数据。

输入go tool pprof -http=:6060 cpu.proflocalhost:6060将被打开。 (为了更清楚,我去掉了pprof.Labels)

让我们深入探讨图形表示。

CPU分析图
CPU分析图

节点颜色、字体大小、边缘粗细等都有不同含义,参考pprof: Interpreting the Callgraph[13]获取更多细节。可视化使我们能够更容易识别和修复性能问题。

单击图中的节点,可以对其进行细化,我们可以根据自己的选择对可视化进行过滤。下面展示了部分内容(focushide等)。

Refine选项
Refine选项

还可以看到其他可视化选项。

View选项
View选项

上面出现了peek和source(作为list命令),因此下面将介绍火焰图(Flame Graph)[14]。火焰图提供了代码时间花费的高级视图。

火焰图
火焰图

每个函数都用一个彩色矩形表示,矩形的宽度与该函数花费的时间成正比。

alt

可以访问Github[15]获取源码。

2.2 内存分析

如果需要向应用程序添加运行时内存分析,必须显式启用。

可以访问Github[16]获取源码。

alt

如果执行go run .,将看到生成的mem.prof文件,可以用之前基准测试部分提到的go tool pprof mem.prof对齐进行分析。

下面将介绍两个更有用的命令treepeek

  • tree显示了执行流的所有调用者和被调用者。
tree输出
tree输出

从而帮助我们识别执行流并找出消耗最多内存的对象。 (不要忘记使用granularity=lines,它提供了更可读的格式。)

  • 如果希望查看特定函数的执行流程,可以使用 peek命令。例如, peek expensiveFunc显示如下
alt
  • 还可以使用pprof web界面进行内存分析。输入 go tool pprof -http=:6060 mem.prof,打开 localhost:6060
内存分析图
内存分析图
3. 基于net/http/pprof[17]的Web分析

runtime/pprof包提供了Go程序性能分析的低级接口,而net/http/pprof为分析提供了更高级的接口,允许我们通过HTTP💃收集程序分析信息,所需要做的就是:

添加net HTTP pprof
添加net HTTP pprof

输入localhost:5555/debug/pprof,就能在浏览器上看到所有可用的分析文件。如果没有使用stdlib,可以查看fiber[18]gin[19]echo[20]的pprof实现。

debug/pprof视图
debug/pprof视图

文档里记录了所有用法和参数[21],我们看一下最常用的。

获取CPU分析数据及技巧
go tool pprof http://localhost:5555/debug/pprof/profile?seconds=30

在CPU分析期间,请注意

runtime.mallogc → 表示可以优化小堆分配的数量。

syscall.Read或者syscall.Write → 表示应用程序在内核模式下花费了大量时间,为此可以尝试I/O缓冲。

获取堆(采样活跃对象内存分配)分析数据及技巧
go tool pprof http://localhost:5555/debug/pprof/heap

go tool pprof http://localhost:5555/debug/pprof/heap?gc=1

就我个人而言,我喜欢用GC参数诊断问题。例如,如果应用程序有内存泄漏问题,可以执行以下操作:

  • 触发GC( 浏览器访问/debug/pprof/heap?gc=1)
  • 下载堆数据, 假设下载文件名为file1
  • 等待几秒或几分钟
  • 再次触发GC( 浏览器访问/debug/pprof/heap?gc=1)
  • 再次下载堆数据, 假设下载文件名为file2
  • 使用 diff_base [22]进行比较
go tool pprof -http=:6060 -diff_base file2 file1
diff_base输出
diff_base输出
获取内存分配(抽样过去所有的内存分配)分析数据及技巧
go tool pprof http://localhost:5555/debug/pprof/allocs

在内存分配分析期间,可以这样做

  • 如果看到 bytes.growSlice,应该考虑使用 sync.Pool
  • 如果看到自定义函数,请检查是否在切片或映射中定义了固定容量。
延伸阅读
  1. pprof Github Readme [23]
  2. Profiling Go Programs by Russ Cox [24]
  3. pprof man page [25]
  4. GopherCon 2019: Dave Cheney — Two Go Programs, Three Different Profiling Techniques [26]
  5. GopherCon 2021: Felix Geisendörfer — Go Profiling and Observability from Scratch [27]
  6. GopherConAU 2019 — Alexander Else — Profiling a go service in production [28]
  7. Practical Go Lessons Profiling Chapter [29]

你好,我是俞凡,在Motorola做过研发,现在在Mavenir做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起交流学习。为了方便大家以后能第一时间看到文章,请朋友们关注公众号"DeepNoMind",并设个星标吧,如果能一键三连(转发、点赞、在看),则能给我带来更多的支持和动力,激励我持续写下去,和大家共同成长进步!

参考资料
[1]

Profiling Go Applications in the Right Way with Examples: https://blog.stackademic.com/profiling-go-applications-in-the-right-way-with-examples-e784526e9481

[2]

runtime/pprof: https://pkg.go.dev/runtime/pprof

[3]

net/http/pprof: https://pkg.go.dev/net/http/pprof

[4]

Resource Contention: https://en.wikipedia.org/wiki/Resource_contention

[5]

Two Sum Algorithm: https://leetcode.com/problems/two-sum

[6]

How to write benchmarks in Go: https://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go

[7]

pprof-example: https://github.com/Abdulsametileri/pprof-examples/tree/main/benchmarking

[8]

pprof: https://linux.die.net/man/1/pprof

[9]

runtime/pprof: https://pkg.go.dev/runtime/pprof

[10]

pprof.Labels: https://pkg.go.dev/runtime/pprof#Labels

[11]

pprof tags: https://github.com/google/pprof/blob/main/doc/README.md#tags

[12]

Profiler labels in Go: https://rakyll.org/profiler-labels

[13]

pprof: Interpreting the Callgraph: https://github.com/google/pprof/blob/main/doc/README.md#interpreting-the-callgraph

[14]

火焰图(Flame Graph): https://github.com/google/pprof/blob/main/doc/README.md#flame-graph

[15]

runtime pprof cpu example: https://github.com/Abdulsametileri/pprof-examples/tree/main/runtimepprof/cpu

[16]

runtime pprof memory example: https://github.com/Abdulsametileri/pprof-examples/tree/main/runtimepprof/mem

[17]

net/http/pprof: https://pkg.go.dev/net/http/pprof

[18]

fiber pprof: https://docs.gofiber.io/api/middleware/pprof

[19]

gin pprof: https://github.com/gin-contrib/pprof

[20]

echo pprof: https://pkg.go.dev/github.com/labstack/echo-contrib/pprof

[21]

net/http/pprof usage examples: https://pkg.go.dev/net/http/pprof#hdr-Usage_examples

[22]

pprof comparing profiles: https://github.com/google/pprof/blob/main/doc/README.md#comparing-profiles

[23]

pprof Github Readme: https://github.com/google/pprof/blob/main/doc/README.md

[24]

Profiling Go Programs by Russ Cox: https://blog.golang.org/2011/06/profiling-go-programs.html

[25]

pprof man page: https://linux.die.net/man/1/pprof

[26]

GopherCon 2019: Dave Cheney — Two Go Programs, Three Different Profiling Techniques: https://www.youtube.com/watch?v=nok0aYiGiYA

[27]

GopherCon 2021: Felix Geisendörfer — Go Profiling and Observability from Scratch: https://www.youtube.com/watch?v=7hg4T2Qqowk

[28]

GopherConAU 2019 — Alexander Else — Profiling a go service in production: https://www.youtube.com/watch?v=19bxBMPOlyA

[29]

Practical Go Lessons Profiling Chapter: https://www.practical-go-lessons.com/chap-36-program-profiling

本文由 mdnice 多平台发布

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

实现VLAN间通信以太网链路聚合与交换机堆叠、集群华为ICT网络赛道

10.实现VLAN间通信 10.1.使用路由器实现VLAN间通信 使用路由器物理接口 路由器三层接口作为网关,转发本网段前往其它网段的流量。 路由器三层接口无法处理携带VLAN Tag的数据帧,因此交换机上联路由器的接口需配置为Access. 路由器的一个物理接口作为一…

两次网脱+疑难白内障,眼科医生刀尖起舞为他挽回光明!

“不错,挺清楚的”“文件能看清了”“墙上的小字也能看见了”…… “好啦好啦,快别嘚瑟了!”妻子在一旁抿嘴笑。 昨天刚做完白内障手术的Y先生,打开纱布后如释重负的心情溢于言表。 同坐在一间复查室里的,还有几位老…

【FPGA】高云FPGA之数字钟实验->HC595驱动数码管

高云FPGA之IP核的使用 1、设计定义2、设计输入2.1 数码管译码显示2.2 74HC595驱动2.3 主模块设计 3、分析和综合4、功能仿真6.1 hex8模块仿真6.2 HC595模块 5、布局布线6、时序仿真7、IO分配以及配置文件(bit流文件)的生成8、配置(烧录&#…

Java实现人事管理系统 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 管理员功能模块2.2 普通员工功能模块2.3 答辩文案 三、系统展示四、核心代码4.1 查询职称4.2 新增留言回复4.3 工资申请4.4 工资审核4.5 员工请假 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的人…

升级打造文物素养知识课堂 猿辅导「博物馆新知计划」第二站正式开启

普及文博知识、历史文化是一个长期的活动,它既需要利用多种方式进行,也需要多方人员共同参与。人们的文化素养的培养是一个长期的过程,是不能一蹴而就的,所以要想提高全民文化素养就要重视青少年的文化素养的培养,文化…

openai公司的chatgpt-3.5参数库内还未增加sora的语料信息

openai公司的chatgpt-3.5参数库内还未增加sora的语料信息!我想通过openai公司的chatgpt3.5来了解一下关于sora的技术信息,结果呢,它竟然回答不知道sora是什么。看来,sora的语料库信息还未来得及加入chatgpt3.5的训练模型中。 如图…

【ArcGIS Pro二次开发】(82):玩个花活_控规指标块生成

一、要实现的效果 废话不多说,这次要实现的是类似控规指标块的标注: 这里只是示例,用了5个格子,做成9个格子也是可以的。 实现这个效果最关键的是要用到Pro中的复合标注。 关于复合标注的用法可以搜一下帮助里的【使用复合注释…

【软件使用】postman使用教程

​ 🍎个人博客:个人主页 🏆个人专栏:软件安装及使用 ⛳️ 功不唐捐,玉汝于成 ​ 目录 前言 正文 步骤1:安装Postman 步骤2:发送请求 步骤3:管理环境变量 步骤4&#xff1…

ADS-B Receiver Module TT-SC1 for UAV and Drones

目录 Introduction Applications Main features Technical parameters Basic technical information Electrical specification Recommended operation conditions General electrical parameters Introduction TT-SC1 is a high quality and low price OEM ADS-B…

ACE 中的Active Object模式

Active Object 设计模式: 1) 根据对象被调用的方式,可以将对象分为两类: Passive Object和Active Object。Passive 和 Object和调用者在同一个线程中,这就是我们通常所用的函数调用。而Active Object和调用在不同的线程中&#xf…

NestJS入门2:创建模块

前文参考: NestJS入门1 1. 创建user模块 在项目目录下输入以下命令 nest g resource user 执行完后会在src文件夹下创建出user文件夹及文件夹下相应的文件,如下 2. 增加打印 3. 测试 (1)POSTBody Postman 服务端的打印 &…

深度学习(16)--基于经典网络架构resnet训练图像分类模型

目录 一.项目介绍 二.项目流程详解 2.1.引入所需的工具包 2.2.数据读取和预处理 2.3.加载resnet152模型 2.4.初始化模型 2.5.设置需要更新的参数 2.6.训练模块设置 2.7.再次训练所有层 2.8.测试网络效果 三.完整代码 一.项目介绍 使用PyTorch工具包调用经典网络架构…

地下管线管网三维建模工具MagicPipe3D V3.4.2发布

经纬管网建模系统MagicPipe3D,本地离线参数化构建地下管网三维模型(包括管道、接头、附属设施等),输出标准3DTiles服务、Obj模型等格式,支持Cesium、Unreal、Unity、Osg等引擎加载进行三维可视化、语义查询、专题分析&…

新版Java面试专题视频教程——框架篇

新版Java面试专题视频教程——框架篇 框架篇 01-框架篇介绍02-Spring-单例bean是线程安全的吗03-Spring-AOP相关面试题04-Spring-事务失效的场景05-Spring-bean的生命周期5.1 BeanDefinition 06-Spring-bean的循环依赖(循环引用)6.1 一般对象的循环依…

算法沉淀——BFS 解决拓扑排序(leetcode真题剖析)

算法沉淀——BFS 解决拓扑排序 01.课程表02.课程表 II03.火星词典 Breadth-First Search (BFS) 在拓扑排序中的应用主要是用来解决有向无环图(DAG)的拓扑排序问题。拓扑排序是对有向图中所有节点的一种线性排序,使得对于每一条有向边 (u, v)&…

数据结构排序:插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序

文章目录 插入排序希尔排序选择排序冒泡排序堆排序快速排序 插入排序 基本思想: 直接插入排序是一种简单的插入排序法,其基本思想是: 把待排序的值按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完…

matlab入门,在线编辑,无需安装matab

matlab相关教程做的很完善,除了B站看看教程,官方教程我觉得更加高效。跟着教程一步一步编辑,非常方便。 阅读 MATLAB 官方教程: MATLAB 官方教程提供了从基础到高级的教学内容,内容包括 MATLAB 的基本语法、数据处理…

AUTOSAR CP--chapter7从CAN网络学习Autosar通信

从CAN网络学习Autosar通信 前言缩写词CAN通信在AUTOSAR架构中的传输上位机配置 第六章总结:学习了如何使用工具的自动配置功能,位我们生成系统描述中部分ecu的BSW模块配置,但是自动配置的功能虽然为我们提供了极大的便利,我们仍然…

Windows 使设置更改立即生效——并行发送广播消息

目录 前言 1 遍历窗口句柄列表 2 使用 SendMessageTimeout 发送延时消息 3 并行发送消息实现模拟广播消息 4 修改 UIPI 消息过滤器设置 5 托盘图标刷新的处理 6 完整代码和测试 本文属于原创文章,转载请注明出处: https://blog.csdn.net/qq_5907…

运筹系列89:anylogic仿真软件入门

1. agent-based simulation 这里概述一下help文档中Supply chain GIS model例子的要点:触发事件的agent和执行任务的agent。 在这个案例中,触发事件的agent是retailer,不断有订单生成;而执行任务的agent是vehicle,不断…