一、性能分析类型

1.CPU性能分析

CPU性能分析是最常见的性能分析类型。启动CPU分析时,运行时每隔10ms中断一次,采集正在运行协程的堆栈信息。

程序运行结束后,可以根据收集的数据,找到最热代码路径。

一个函数在分析阶段出现的次数越多,则该函数的代码路径(code path)花费的时间占总运行时间的比重越大。

2.内存性能分析

内存性能分析记录堆内存分配信息,忽略栈内存的分配。

内存分析启动时,默认每1000次采样1次,这个比例是可以调整的。因为内存性能分析是基于采样的,因此基于内存分析数据来判断程序所有的内存使用情况是很困难的。

3.阻塞性能分析

阻塞性能分析是go特点的。

阻塞性能分析用来记录一个协程用来等待共享资源所花费的时间,这用来判断程序并发瓶颈是很有用。阻塞的场景包括:

  • 在没有缓冲的信道上发送或接受数据。
  • 在空的信道上接受数据或在满的信道上发送数据。
  • 尝试获取一个已被其他协程占用的排他锁。

一般情况下,当所有的 CPU 和内存瓶颈解决后,才会考虑这一类分析。

二、cpu性能分析

1.生成pporf

go 性能分析接口位于runtime/pprof 中:

测试代码:生成5组数据,进行冒泡排序:

main.go

想要度量这段代码的性能,只需要在main函数最前加两行代码:

main()

go run main.go > cpu.pprof

当然也可以将输出直接导入到文件中:

2.分析数据

此时得到cpu.pprof 文件:

go tool pprof -http=:9999 cpu.pprof 如果提升Graphviz没有安装: apt installgraphviz (ubuntu)

访问localhost:9999 得到:

除了在网页中查看外,还可以使用交互式命令进行查看:

go tool pprof cpu.pprof

使用top 查看到 bubbleSort函数占用cpu最多。

还可以使用top --cum,按照cum(累计消耗)排序:

使用help 查看帮助:

三、内存性能分析

下面为一段字符串拼接代码,我们对它进行内存分析:

我们使用另外一个性能分析库"github.com/pkg/profile" 它内部封装了 runtime/pprof 接口,使用起来更加简单。

cpu性能分析:

内存性能分析:

profile包会自动在/tmp目录下生成profile文件

go tool pprof -http=:9999 /tmp/profile575547387/mem.pprof

可以看见concat 消耗了 524 KB, 而randomString消耗了 21KB,为什么相差这么大呢?

因为go中的字符串不可修改,使用+ 连接字符串会导致重新生成新的字符串,将 + 两边的子字符串拷贝到新的字符串去。那这种设计多次字符串拼接的场景该如何优化呢?使用strings.Builder

优化后的代码:

优化后可以看到concat 函数使用了71KB 内存,randomString函数使用了 21kb 内存。

四、benchmark 生成 profile

使用benchmark 进行基准测试时,除了直接查看结果,还可以生成profile

testing支持cpu、mem、block

  • -cpuprofile=$FILE
  • -memprofile=$FILE, -memprofilerate=N 调整记录速率为原来的 1/N。
  • -blockprofile=$FILE

fib_test.go

go test -bench=. test/bench/fib -cpuprofile=cpu.pprof

go tool pprof -test cpu.pprof

go tool pprof 支持多种输出格式:

go tool pprof