今天上午参加了Go语言项目开发成员Dave Cheney的分享。
分享的主题是”High Performance Go: Two tools, three types of profiling in 45 minutes”
分享的内容来自 https://dave.cheney.net/high-performance-go-workshop/gopherchina-2019.html 非常有干货的分享!
两个工具分别是pprof和trace,三种类型的分析例子cpu性能分析,内存性能分析,trace性能分析
Dave首先感谢了Tencent Tarscloud,估计是Tars项目的同事邀请他来的。
通过一个统计文件内容中单词的个数的go程序和linux自带的wc的性能对比来展示go的性能分析工具pprof的用法。
原始程序代码如下:
编译运行后统计《Moby Dick》白鲸记这篇小说的单词个数。(moby dick直译是大雕???《白鲸记》为赫尔曼·梅尔维尔发表于1851年的小说,被认为是美国最伟大的长篇小说之一。是一部以海上捕鲸业为题材的小说,一位名叫亚哈的“裴廓德号”捕鲸船船长带领全体船员,追捕一条叫做“莫比·迪克”的大白鲸的历险过程。https://www.gutenberg.org/files/2701/2701-h/2701-h.htm)该程序大约2秒统计完成,而wc只要0.012秒,性能差距太大,经过分析优化后,性能接近wc,不如wc我猜测应该是wc的统计逻辑相对简单导致的,他们的统计结果其实并不一样。
于是在代码中开始CPU性能分析,添加profile代码:
go默认的pprof有两个包:net/http/pprof
和runtime/pprof
,前者是通过后者实现的。这里的pkg/profile
是第三方包,也是通过runtime/pprof
实现的,更加好用。
使用go run
运行后会生成cpu.pprof
文件(go build
不行),接下来就可以通过执行命令 go tool pprof /path/to/cpu.pprof
来展示分析结果,执行top命令:
可以通过在上面的命令中添加-http=:PORT
参数来启动http服务,就会自动打开浏览器在里面展示dot图,里面可以交互可视化的查看top,火焰图,调用图,源码信息,很强大,需要先安装Graphviz,注意添加bin目录到Path中才行。
我这里测试是windows本地创建了一个有320万个英文字符文件,字符数量太少效果不太明显,调用图如下(和*nix上的输出有所不同):
上面的graph可以看到cpu 90+%是在做syscall.Syscall
,因为readbyte直接对文件对象进行读取操作,每次读一个单词,于是这个程序有多少个单词就会调用多少次syscall,系统调用通常都是比较昂贵的操作,大量的syscall就占用了cpu,导致程序性能下降。
优化:因为readbyte接收的是io.Reader的interface,所以可以把文件对象f通过bufio缓存起来就不用每次去读取文件导致syscall了。bufio实现了io.Reader和io.Writer。
性能分析数据
本地测试文件优化以前有大量syscall大概要62秒,优化后没有产生syscall,只需要860毫秒左右,主要耗时在readbyte里创建buf对象。
总结:通过pprof工具可以分析出cpu瓶颈所在。频繁的文件操作会是syscall大量占用cpu,可以使用bufio避免syscall大幅优化性能。
profile.Start默认分析cpu,其他指标需要显示指定参数,内存分析rate设置为1表示搜集所有的内存分配信息
可以看到程序内存主要分配在readbyte这里,这里windows上的测试结果和dave分享的结果不太一样。
项目地址: https://github.com/campoy/mandelbro
运行这个程序会生成一张图片,大约花了1.6秒。如何评估它的性能好坏?是否可能让它更快?
首先还是通过pprof分析cpu性能
可以看到cpu消耗在seqFillg中调用fillPixel函数上
在代码中加入trace分析
编译成可执行文件,然后执行,会生成trace.out文件,现在可以使用trace工具分析该文件 go tool trace trace.out
会自动打开浏览器,页面上有查看trace,gorutine分析,网络阻塞分析,同步阻塞分析,系统调用阻塞分析,调度耗时分析等
页面显示只利用了一个cpu
使用gorutine来利用多个cpu
oneToOne方法
每一次的fillpixel都开一个gorutine,运行时间并没有缩短,在trace页面放大看,虽然使用了多个cpu,到cpu并不是连续的,全部是断开的,因为fillpixel做的事太少,花费了大量的时间在gorutine的调度上。
onePerRow方法
将一行像素的填充作为一个gorutine,运行耗时大幅减少,再看trace页面,cpu也利用的不错,gorutine数量也不多。
最后还有一个nworker方法
执行耗时会最慢,因为它使用的是无缓冲channel,每一次都得等。修改为有缓冲的channel就会大幅提升性能。
trace信息也可以通过http接口获取 curl -o trace.out http://127.0.0.1:8080/debug/pprof/trace?seconds=5
接下来是QA环节,同学们纷纷用流利的英语和Dave交流 :)
最后是Tars团队和Dave一起坐上讲台不知道要干嘛,我就先溜了。
网友185.*.*.38[火星]2022-06-30 05:53
网友185.*.*.22[火星]2022-06-30 05:53
网友51.*.*.12[火星]2022-06-30 05:52
网友157.*.*.226[Redmond]2022-06-30 05:44
发表评论
亲~ 评论内容是必须的哟! o(∩_∩)o
昵称
邮箱
主页
评论