go语言的性能调优|青训营

97 阅读4分钟

前言

想要进行性能优化,首先瞩目在 Go 自身提供的工具链来作为分析依据:

  • net/http/pprof:采集 HTTP Server 的运行时数据进行分析

pprof开启后,每隔一段时间(10ms)就会收集下当前的堆栈信息,获取格格函数占用的CPU以及内存资源;最后通过对这些采样数据进行分析,形成一个性能分析报告。

操作

  1. 导入pprof包 首先,我们需要在代码中导入pprof包。在需要进行性能分析的地方,我们可以插入pprof的代码,用来记录相关的性能数据。
  2. 启动pprof服务 在代码中添加启动pprof服务的代码,这样我们就可以通过浏览器访问pprof的可视化界面,查看性能数据。例如,我们可以使用以下代码来启动pprof服务:
import (
        _ "net/http/pprof"
        )
​
func main() {
    go func() {
        if err := http.ListenAndServe(":6060", nil); err != nil {
            log.Fatal(err)
        }
        //...
}

运行后,即可进行性能数据查看

浏览器输入localhost:端口+/debug/pprof/,该界面可以看到内存、block、协程、堆等分配的情况以及锁的操作等。

image-20230830090718138.png

打开命令行,输入 go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10"即可进入pprof工具

image-20230830091106175.png

  1. 收集性能数据 在应用程序运行时,我们可以使用命令行工具或浏览器访问pprof服务来收集性能数据。以下是一些常用的命令行工具:

  2. go tool pprof:用于分析CPU和内存数据。

  3. go-torch:用于生成火焰图,帮助我们可视化代码的热点。

  4. go tool trace:用于分析代码的执行过程。

  5. 分析性能数据 一旦我们收集到性能数据,就可以使用pprof工具来进行分析。pprof提供了丰富的命令和选项,可以帮助我们深入理解应用程序的性能问题。一些常用的pprof命令如下:

  6. top:显示耗时最高的函数列表,例如:

image-20230830091205134.png

以上内容分析:

`flat`: 当前函数本身的执行耗时
`flat%``flat``CPU`总时间的比例
`sum%`: 上面每一行的`flat%`总和
`cum`: 当前函数本身加上其周期函数的总耗时
`cum%``cum``CPU`总时间的比例
  1. 或者通过web进行访问:输入命令web,可以看到可视化的性能分析

image-20230830091847999.png

pprof的采样过程和原理

CPU采样过程:

采样对象:函数调用和他们占用的时间

采样率:100次/秒,固定值

采样时间:从手动启动到手动结束

操作系统:每10ms向进程发送一次SIGPROF信号

进程:每次接收到SIGPROF会记录调用堆栈

写缓冲:每100ms读取已经记录的调用栈并写入输出流

Heap-堆内存采样过程:

采样程序通过内存分配器在堆上分配和释放内存,记录分配/释放的大小和数量

采样率:每分配512KB记录一次,可在运行开头修改,1为每次分配均记录

采样时间:从程序运行开始到采样时

采样指标:alloc_space, alloc_objects, inuse_space, inuse_objects

计算方式:inuse = alloc - free

Goroutine-协程&ThreadCreate-线程创建

Goroutine:记录所有用户发起且在运行中的goroutine(即入口非runtime开头的)runtime.main的调用栈信息

ThreadCreate:记录程序创建的所有系统线程的信息

Block-阻塞&Mutex-锁

阻塞操作——采样阻塞操作的次数和耗时

采样率:阻塞耗时超过阈值的才会被记录,1为每次阻塞均记录

锁竞争——采样争抢锁的次数和耗时

采样率:只记录固定比例的锁操作,1为每次加锁均记录

常见的优化技巧

除了使用pprof工具进行性能分析外,还可以采取一些常见的优化技巧来提升Go应用程序的性能。还有一些常见的优化技巧:

  1. 减少内存分配:尽量避免频繁的内存分配和垃圾回收操作。
  2. 使用并发编程:利用Go语言的并发特性来提高程序的并行性和吞吐量。
  3. 减少系统调用:避免不必要的系统调用,尽量使用内存缓存和异步IO等技术减少IO操作。
  4. 优化算法和数据结构:选择高效的算法和数据结构,能够显著提升程序的性能。