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

什么是性能分析?
性能分析(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参数运行项目中的所有基准测试。

根据上面的输出,与其他方法相比,TwoSumWithBruteForce
是最有效的方法。别忘了结果取决于函数输入,如果输入一个大数组,会得到不同的结果。😅
如果输入go help testflag
,将看到许多参数及其解释,比如count
、benchtime
等,后面将解释最常用的参数。
-
如果要运行特定函数,可以通过如下方式指定:
go test -bench='BenchmarkTwoSumWithBruteForce'
-
默认情况下,基准测试函数只运行一次。如果要自定义,可以使用 count
参数。例如,
go test -bench='.' -count=2
输出如下所示。

-
默认情况下,Go决定每个基准测试操作的运行时间,可以通过自定义 benchtime='2s'
指定。
可以同时使用count
和benchtime
参数,以便更好的度量基准函数。请参考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交互式控制台。

我们来看看最主要的内容。
-
输入 top15
查看执行期间排名前15的资源密集型函数。 (15表示显示的节点数。)

为了解释清楚,假设有一个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=cum
和top15
命令。 -
如果通过 top
获得更详细的输出,可以指定granularity
选项。例如,如果设置granularity=lines
,将显示函数的行。

得益于此,我们可以识别导致性能问题的函数的特定行。😌
-
输出还显示了运行时函数和用户自定义函数。如果只想关注自己的函数,可以设置 hide=runtime
并再次执行top15
。

可以通过输入hide=
来重置。
-
此外,可以使用 show
命令。例如,输入show=TwoSum

-
如果只关注指定节点,可以使用 focus
命令。例如关注TwoSumOnePassHashTable
,显示为

可以输入focus=
来重置。
-
如果需要获取该功能的详细信息,可以使用 list
命令。例如,想获得关于TwoSumWithTwoPassHashTable
函数的详细信息,输入list TwoSumWithTwoPassHashTable

-
如果要查看图形化的调用栈,可以键入 web
。


后面将提供更多关于分析图表的细节。
-
还可以键入 gif
或pdf
以与他人共享相应格式的分析数据。😃
1.2 内存分析
如果输入go tool pprof mem.prof
并回车


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

-
web命令

可以使用CPU分析部分中提到的所有命令。
下面看一下另一个方法,runtime/pprof。🚀
2. 基于runtime/pprof[9]的运行时分析
基准测试对单个函数的性能很有用,但不足以理解整体情况,这时就需要用到runtime/pprof💠。
2.1 CPU分析
基准测试内置CPU和内存分析,但如果需要让应用程序支持运行时CPU分析,必须首先显示启用。

如果执行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
,将显示带了有用信息的标记函数。

可以用标签做很多事情[11],阅读Profiler labels in Go[12]可以获得更多信息。
pprof还有很棒的web界面,允许我们使用各种可视化方式分析数据。
输入go tool pprof -http=:6060 cpu.prof
,localhost:6060
将被打开。 (为了更清楚,我去掉了pprof.Labels)
让我们深入探讨图形表示。

节点颜色、字体大小、边缘粗细等都有不同含义,参考pprof: Interpreting the Callgraph[13]获取更多细节。可视化使我们能够更容易识别和修复性能问题。
单击图中的节点,可以对其进行细化,我们可以根据自己的选择对可视化进行过滤。下面展示了部分内容(focus、hide等)。

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

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

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

可以访问Github[15]获取源码。
2.2 内存分析
如果需要向应用程序添加运行时内存分析,必须显式启用。
可以访问Github[16]获取源码。

如果执行go run .
,将看到生成的mem.prof
文件,可以用之前基准测试部分提到的go tool pprof mem.prof
对齐进行分析。
下面将介绍两个更有用的命令tree
和peek
。
-
tree
显示了执行流的所有调用者和被调用者。

从而帮助我们识别执行流并找出消耗最多内存的对象。 (不要忘记使用granularity=lines
,它提供了更可读的格式。)
-
如果希望查看特定函数的执行流程,可以使用 peek
命令。例如,peek expensiveFunc
显示如下

-
还可以使用pprof web界面进行内存分析。输入 go tool pprof -http=:6060 mem.prof
,打开localhost:6060
。

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

输入localhost:5555/debug/pprof
,就能在浏览器上看到所有可用的分析文件。如果没有使用stdlib,可以查看fiber[18]、gin[19]或echo[20]的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

获取内存分配(抽样过去所有的内存分配)分析数据及技巧
go tool pprof http://localhost:5555/debug/pprof/allocs
在内存分配分析期间,可以这样做
-
如果看到 bytes.growSlice
,应该考虑使用sync.Pool
。 -
如果看到自定义函数,请检查是否在切片或映射中定义了固定容量。
延伸阅读
-
pprof Github Readme [23] -
Profiling Go Programs by Russ Cox [24] -
pprof man page [25] -
GopherCon 2019: Dave Cheney — Two Go Programs, Three Different Profiling Techniques [26] -
GopherCon 2021: Felix Geisendörfer — Go Profiling and Observability from Scratch [27] -
GopherConAU 2019 — Alexander Else — Profiling a go service in production [28] -
Practical Go Lessons Profiling Chapter [29]
你好,我是俞凡,在Motorola做过研发,现在在Mavenir做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起交流学习。为了方便大家以后能第一时间看到文章,请朋友们关注公众号"DeepNoMind",并设个星标吧,如果能一键三连(转发、点赞、在看),则能给我带来更多的支持和动力,激励我持续写下去,和大家共同成长进步!
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 多平台发布
相关文章:

Go应用性能分析实战
Go很适合用来开发高性能网络应用,但仍然需要借助有效的工具进行性能分析,优化代码逻辑。本文介绍了如何通过go test benchmark和pprof进行性能分析,从而实现最优的代码效能。原文: Profiling Go Applications in the Right Way with Examples…...
MySQL的索引类型
目录 1. 主键索引 (PRIMARY KEY) 2. 唯一索引 (UNIQUE) 3. 普通索引 (INDEX) 4. 全文索引 (FULLTEXT) 5. 空间索引 (SPATIAL) 6. 组合索引 (COMPOSITE INDEX) 7. 前缀索引 (PREFIX INDEX) 8. 覆盖索引 (COVERING INDEX) 1. 主键索引 (PRIMARY KEY) 描述:表…...

picker选择器-年月日选择
从底部弹起的滚动选择器。支持五种选择器,通过mode来区分,分别是普通选择器,多列选择器,时间选择器,日期选择器,省市区选择器,默认是普通选择器。 学习一下日期选择器 平台差异说明 日期选择默…...
【LeetCode-494】目标和(回溯动归)
目录 LeetCode494.目标和 题目描述 解法1:回溯法 代码实现 解法2:动态规划 代码实现 LeetCode494.目标和 题目链接 题目描述 给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 和 -。对于数组中…...

力扣 188. 买卖股票的最佳时机 IV
题目来源:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iv/description/ C题解:动态规划 思路同力扣 123. 买卖股票的最佳时机 III-CSDN博客,只是把最高2次换成k次。如果思路不清晰,可以将k从0写到4等找找规律…...

【Go语言】Go项目工程管理
GO 项目工程管理(Go Modules) Go 1.11 版本开始,官方提供了 Go Modules 进行项目管理,Go 1.13开始,Go项目默认使用 Go Modules 进行项目管理。 使用 Go Modules的好处是不再需要依赖 GOPATH,可以在任意位…...

美容小程序:让预约更简单,服务更贴心
在当今繁忙的生活节奏中,美容预约常常令人感到繁琐和疲惫。为了解决这个问题,许多美容院和SPA中心已经开始采用美容小程序来简化预约流程,并提供更加贴心的服务。在这篇文章中,我们将引导您了解如何制作一个美容小程序,…...

【递归】:原理、应用与案例解析 ,助你深入理解递归核心思想
递归 1.基础简介 递归在计算机科学中,递归是一种解决计算问题的方法,其中解决方案取决于同一类问题的更小子集 例如 递归遍历环形链表 基本情况(Base Case):基本情况是递归函数中最简单的情况,它们通常是递…...

【 Maven 】花式玩法之多模块项目
目录 一、认识Maven多模块项目 二、maven如何定义项目的发布策略 2.1 版本管理 2.2 构建配置 2.3 部署和发布 2.4 依赖管理 2.5 发布流程 三、使用Jenkins持续集成Maven项目 四、总结 如果你有一个多模块项目,并且想将这些模块发布到不同的仓库或目标位置&…...

LeetCode 热题 100 Day01
哈希模块 哈希结构: 哈希结构,即hash table,哈希表|散列表结构。 图摘自《代码随想录》 哈希表本质上表示的元素和索引的一种映射关系。 若查找某个数组中第n个元素,有两种方法: 1.从头遍历,复杂度…...

[vscode]vue js部分结尾加分号
设置中寻找 semicolons确定在TypeScript的这个扩展中设置选项为insert...

友点CMS image_upload.php 文件上传漏洞复现
0x01 产品简介 友点CMS是一款高效且灵活的网站管理系统,它为用户提供了简单易用的界面和丰富的功能。无论是企业还是个人,都能通过友点CMS快速搭建出专业且美观的网站。该系统支持多种内容类型和自定义模板,方便用户按需调整。同时,它具备强大的SEO功能,能提升网站在搜索…...

C语言—指针(3)
嘿嘿嘿嘿,你看我像指针吗? 不会写,等我啥时候会写了再说吧,真的累了,倦了 1.面试题 1)定义整形变量i; 2)p为指向整形变量的指针变量; 3)定…...

【八股文】面向对象基础
【八股文】面向对象基础 面向对象和面向过程的区别 面向过程把解决问题的过程拆成一个个方法,通过一个个方法的执行解决问题。面向对象会先抽象出对象,然后用对象执行方法的方式解决问题。 创建一个对象用什么运算符?对象实体与对象引用有何不同? …...
Day49 647 回文子串 516 最长回文子序列
647 回文子串 给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。 方法一:动态规划: 采用一个二维的dp数组…...
探秘GNU/Linux Shell:命令行的魔法世界
GNU/Linux的Shell是一种特殊的交互式工具,为用户提供了强大的控制和管理Linux系统的方式。在这个博客中,我们将深入了解Shell的基本概念、功能以及不同类型的Shell。 Shell的本质 Shell的核心是命令行提示符,它是用户与Linux系统进行交互的…...

基于STM32F407的coreJSON使用教程
目录 概述 工程建立 代码集成 函数介绍 使用示例 概述 coreJSON是FreeRTOS中的一个组件库,支持key查找的解析器,他只是一个解析器,不能生成json数据。同时严格执行 ECMA-404 JSON 标准。该库用 C 语言编写,设计符合 ISO C90…...

keepalived双主模式测试
文章目录 环境准备部署安装keepavlived配置启动测试模拟Nginx宕机重新启动问题分析 环境准备 测试一下keepalived的双主模式,所谓双主模式就是两个keepavlied节点各持有一个/组虚IP,默认情况下,二者互为主备,同时对外提供服务&am…...
微服务中的熔断、降级和限流
在现代微服务架构中,熔断、降级和限流是保障系统稳定性和可靠性的重要手段。本文将深入探讨这三种机制在微服务架构中的作用、原理以及实践方法。 1. 熔断(Circuit Breaker) 1.1 作用和原理 熔断器是一种可以在服务发生故障时快速中断请求的机制,防止故障蔓延到整个系统…...

2023年便宜的云服务器分享:最低26元4核16G
2024年阿里云服务器租用价格表更新,云服务器ECS经济型e实例2核2G、3M固定带宽99元一年、ECS u1实例2核4G、5M固定带宽、80G ESSD Entry盘优惠价格199元一年,轻量应用服务器2核2G3M带宽轻量服务器一年61元、2核4G4M带宽轻量服务器一年165元12个月、2核4G服…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...

初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...

vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...