go的性能分析:pprof工具

简介: go的性能分析:pprof工具

pprof

pprof是GoLang程序性能分析工具,prof是profile(画像)的缩写 .通过pprof,我们可以得到程序执行的以下数据:

Profile Descriptions:

  • allocs:
    内存分配数据采样信息
  • block:
    导致同步原语阻塞的堆栈跟踪
  • cmdline:
    当前程序的命令行调用
    goroutine:
    所有当前goroutine的堆栈跟踪
  • heap:
    活动对象的内存分配采样。您可以指定gcGET参数以在获取堆样本之前运行gc。
  • mutex:
    争用互斥锁持有者的堆栈跟踪
  • profile:
    CPU配置文件。可以在秒GET参数中指定持续时间。获取配置文件后,使用go工具pprof命令调查配置文件。
  • threadcreate:
    导致创建新操作系统线程的堆栈跟踪
  • trace:
    A trace of execution of the current program. You can specify the duration in the seconds GET parameter. After you get the trace file, use the go tool trace command to investigate the trace.

真正分析时常用4种

  • CPU Profiling:CPU 分析,按照一定的频率采集所监听的应用程序 CPU(含寄存器)的使用情况,可确定应用程序在主动消耗 CPU 周期时花费时间的位置
  • Memory Profiling:内存分析,在应用程序进行堆分配时记录堆栈跟踪,用于监视当前和历史内存使用情况,以及检查内存泄漏
  • Block Profiling:阻塞分析,记录 goroutine 阻塞等待同步(包括定时器通道)的位置
  • Mutex Profiling:互斥锁分析,报告互斥锁的竞争情况

做性能分析,第一步需要先获取数据,然后对数据进行分析。所以下面展示一下如何进行数据获取。

数据获取方式

go的运行有2种情况,一种是常驻内存 服务式运行,例如http,tcp服务,一直运行接收请求,一种是工具脚本形式,运行完则退出

工具型应用

工具型应用调用比较简单,主要是用 runtime/pprof库,将画像数据写入文件中:

package main
import (
   "os"
   "runtime/pprof"
   _ "runtime/pprof"
)
func main() {
   //获取cpu profile
   cpuFile, _ := os.OpenFile("cpu.prof", os.O\_CREATE|os.O\_RDWR, 0644)
   defer cpuFile.Close()
   pprof.StartCPUProfile(cpuFile)
   defer pprof.StopCPUProfile()
   //获取内存profile
   memFile, _ := os.OpenFile("mem.prof", os.O\_CREATE|os.O\_RDWR, 0644)
   defer memFile.Close()
   pprof.WriteHeapProfile(memFile)
   //执行循环1000次
   for i := 0; i < 1000; i++ {
   }
}

执行完毕之后,可以直接看到cpu.prof和mem.prof文件,里面包含了画像数据:

(base) tioncico@appledeMacBook-Pro test % ls *.prof
cpu.prof        mem.prof

文件不能直接查看,需要通过go tool pprof 工具读取显示,在下面会讲到

服务型应用

服务型应用通过 "net/http/pprof"库进行获取,在http库中,默认使用了defaultServerMux,可以直接使用即可获取:

package main
import (
   "fmt"
   "net/http"
   _ "net/http/pprof"
)
func hello(w http.ResponseWriter, r *http.Request) {
   fmt.Fprintf(w, "hello world!")
}
func main() {
   http.HandleFunc("/", hello)              //设置访问的路由
   err := http.ListenAndServe(":8080", nil) //设置监听的端口
   if err != nil {
      fmt.Printf("ListenAndServe: %s", err)
   }
}

访问 http://localhost:8080  可以输出 hello world

访问 http://localhost:8080/debug/pprof/  可以查看到画像信息:

serverMux

可以看出,本身没有注册/debug/pprof,但是直接访问依然还是有数据的,那是因为在 http.ListenAndServe 时,没有传入自定义的serverMux进行路由处理,则pprof库自动进行了注册:

image.png

如果你使用了自定义的serverMux,则需要自己注册,才能获取到pprof

http.HandleFunc("/debug/ppprof/", pprof.Index)

这样的话,访问 http://localhost:8080/debug/ppprof/ 也是有用的

开源框架

在不同的开源框架中,有提供自己封装好的pprof包,调用更加方便,具体使用请参考框架文档

pprof主要核心就是将pprof路由注册到服务中,并可以访问此服务即可

数据分析

数据分析通过命令  go tool pprof 进行,主要有2种查看模式

1:通过http路径进入交互模式

2:通过文件进入交互模式或者进入web页面查看

交互模式

进入命令行,基于http路径的数据分析:

(base) tioncico@appledeMacBook-Pro test % go tool pprof http://localhost:8080/debug/pprof/profile
Fetching profile over HTTP from http://localhost:8080/debug/pprof/profile
Saved profile in /Users/tioncico/pprof/pprof.samples.cpu.003.pb.gz
Type: cpu
Time: Nov 22, 2022 at 5:16pm (CST)
Duration: 30s, Total samples = 20ms (0.067%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)

执行此命令后,会采集默认30秒之后的cpu数据,然后生成记录,进行数据分析,可以通过?=seconds=60改成60秒

也可以通过 go tool pprof cpu.prof(文件名) 方式进入

进入命令行交互模式之后,可以使用help查看所有命令,通过help <cmd|option> 查看子命令使用方法

(pprof) help
  Commands:
    callgrind        Outputs a graph in callgrind format
    comments         Output all profile comments
    disasm           Output assembly listings annotated with samples
    dot              Outputs a graph in DOT format
    eog              Visualize graph through eog
    evince           Visualize graph through evince
    gif              Outputs a graph image in GIF format
    gv               Visualize graph through gv
    kcachegrind      Visualize report in KCachegrind
    list             Output annotated source for functions matching regexp
    pdf              Outputs a graph in PDF format
    peek             Output callers/callees of functions matching regexp
    png              Outputs a graph image in PNG format
    proto            Outputs the profile in compressed protobuf format
    ps               Outputs a graph in PS format
    raw              Outputs a text representation of the raw profile
    svg              Outputs a graph in SVG format
    tags             Outputs all tags in the profile
    text             Outputs top entries in text form
    top              Outputs top entries in text form
    topproto         Outputs top entries in compressed protobuf format
    traces           Outputs all profile samples in text form
    tree             Outputs a text rendering of call graph
    web              Visualize graph through web browser
    weblist          Display annotated source in a web browser
    o/options        List options and their current values
    q/quit/exit/^D   Exit pprof

可以自行研究

通过top,可以查看到cpu占用最高的排序:

(pprof) top
Showing nodes accounting for 20ms, 100% of 20ms total
      flat  flat%   sum%        cum   cum%
      10ms 50.00% 50.00%       10ms 50.00%  runtime.libcCall
      10ms 50.00%   100%       20ms   100%  runtime.pthread\_cond\_wait
         0     0%   100%       20ms   100%  runtime.findrunnable
         0     0%   100%       20ms   100%  runtime.mPark
         0     0%   100%       20ms   100%  runtime.mcall
         0     0%   100%       20ms   100%  runtime.notesleep
         0     0%   100%       20ms   100%  runtime.park_m
         0     0%   100%       20ms   100%  runtime.schedule
         0     0%   100%       20ms   100%  runtime.semasleep
         0     0%   100%       20ms   100%  runtime.stopm
(pprof)

这个东西看不出啥,我们改造下http服务的helloworld,使其计算1000次快速排序:

package main
import (
   "fmt"
   "math/rand"
   "net/http"
   _ "net/http/pprof"
)
func hello(w http.ResponseWriter, r *http.Request) {
   //生成一个100个随机数字的数组
   arr := make(\[\]int, 100)
   for i := 0; i < 100; i++ {
      arr\[i\] = rand.Intn(100)
   }
   for i := 0; i < 10000; i++ {
      //快速排序
      quickSort(arr, 0, len(arr)-1)
   }
   fmt.Fprintf(w, "hello world!")
}
func main() {
   http.HandleFunc("/", hello)              //设置访问的路由
   err := http.ListenAndServe(":8080", nil) //设置监听的端口
   if err != nil {
      fmt.Printf("ListenAndServe: %s", err)
   }
}
//快速排序
func quickSort(arr \[\]int, left, right int) {
   if left < right {
      pivot := partition(arr, left, right)
      quickSort(arr, left, pivot-1)
      quickSort(arr, pivot+1, right)
   }
}
func partition(arr \[\]int, left int, right int) int {
   pivot := arr\[left\]
   for left < right {
      for left < right && arr\[right\] >= pivot {
         right--
      }
      arr\[left\] = arr\[right\]
      for left < right && arr\[left\] <= pivot {
         left++
      }
      arr\[right\] = arr\[left\]
   }
   arr\[left\] = pivot
   return left
}

重新执行分析命令进行分析,top命令查看:

(pprof) top
Showing nodes accounting for 144.72s, 99.70% of 145.16s total
Dropped 21 nodes (cum <= 0.73s)
      flat  flat%   sum%        cum   cum%
   114.47s 78.86% 78.86%    114.83s 79.11%  main.partition
    30.03s 20.69% 99.55%    144.90s 99.82%  main.quickSort
     0.22s  0.15% 99.70%    117.12s 80.68%  main.hello
         0     0% 99.70%    114.42s 78.82%  net/http.(*ServeMux).ServeHTTP
         0     0% 99.70%    111.58s 76.87%  net/http.(*conn).serve
         0     0% 99.70%    115.71s 79.71%  net/http.HandlerFunc.ServeHTTP
         0     0% 99.70%    112.95s 77.81%  net/http.serverHandler.ServeHTTP
(pprof)

每一行都代表一个函数的信息,每列的标识为:

flat:函数在 CPU 上运行的时间

flat%:函数在CPU上运行时间的百分比

sum%:是从上到当前行所有函数累加使用 CPU 的比例,如第二行sum=48.52=28.79+19.73

cum:这个函数以及子函数运行所占用的时间,应该大于等于flat

cum%:这个函数以及子函数运行所占用的比例,应该大于等于flat%

最后一列:函数的名字

如果应用程序有性能问题,上面这些信息应该能告诉我们时间都花费在哪些函数的执行上

查看函数具体执行问题

通过 交互模式 list 函数名,即可查看到该函数的具体哪一行耗时:

(pprof)  list hello      
Total: 145.16s
ROUTINE ------------------------ main.hello in /Users/tioncico/go/src/test/server.go
     220ms    117.12s (flat, cum) 80.68% of Total
         .          .     11:   //生成一个100个随机数字的数组
         .          .     12:   arr := make(\[\]int, 100)
         .          .     13:   for i := 0; i < 100; i++ {
         .          .     14:           arr\[i\] = rand.Intn(100)
         .          .     15:   }
     210ms      210ms     16:   for i := 0; i < 10000; i++ {
         .          .     17:           //快速排序
         .    116.90s     18:           quickSort(arr, 0, len(arr)-1)
         .          .     19:   }
      10ms       10ms     20:   fmt.Fprintf(w, "hello world!")
         .          .     21:}
         .          .     22:
         .          .     23:func main() {
         .          .     24:   http.HandleFunc("/", hello)              //设置访问的路由
         .          .     25:   err := http.ListenAndServe(":8080", nil) //设置监听的端口
(pprof)

可看到,for循环里面的quickSort耗时比较长

生成函数调用图

可以通过svg命令,生成一个svg文件,拖动到浏览器打开即可查看函数调用图,但是需要安装 graphviz 才可以使用,具体安装方法可以自行百度

mac安装方法:

brew install graphviz
go get -u github.com/ofabry/go-callvis

image.png

内存画像

执行go tool pprof http://localhost:8080/debug/pprof/heap  即可,其他和cpu操作同理

web页面模式

通过-http 选项进入web页面:

go tool pprof  -http=:9091 http://localhost:8080/debug/pprof/profile 
go tool pprof  -http=:9091 文件名


image.png

其他可以执行了解探索

目录
相关文章
|
1月前
|
网络协议 Linux Go
分享一个go开发的工具-SNMP Server
分享一个go开发的工具-SNMP Server
27 0
|
2月前
|
算法 Unix Linux
【C/C++ 实用工具】性能分析工具一览
【C/C++ 实用工具】性能分析工具一览
46 0
|
2月前
|
测试技术 API 开发者
【Docker项目实战】在Docker环境下部署go-file文件分享工具
【2月更文挑战第15天】在Docker环境下部署go-file文件分享工具
73 1
|
2月前
|
数据可视化 关系型数据库 编译器
【C/C++ 单线程性能分析工具 Gprof】 GNU的C/C++ 性能分析工具 Gprof 使用全面指南
【C/C++ 单线程性能分析工具 Gprof】 GNU的C/C++ 性能分析工具 Gprof 使用全面指南
122 2
|
14天前
|
Web App开发 JavaScript 前端开发
JavaScript中的性能优化:代码优化技巧与性能分析工具
【4月更文挑战第22天】本文探讨JavaScript性能优化,包括代码优化技巧和性能分析工具。建议避免全局查找、减少DOM操作、使用事件委托、优化循环和异步编程以提升代码效率。推荐使用Chrome DevTools、Lighthouse和jsPerf等工具进行性能检测和优化。持续学习和实践是提升JavaScript应用性能的关键。
|
2天前
|
缓存 Linux
linux性能分析之内存分析(free,vmstat,top,ps,pmap等工具使用介绍)
这些工具可以帮助你监视系统的内存使用情况、识别内存泄漏、找到高内存消耗的进程等。根据具体的问题和需求,你可以选择使用其中一个或多个工具来进行内存性能分析。注意,内存分析通常需要综合考虑多个指标和工具的输出,以便更好地理解系统的行为并采取相应的优化措施。
16 5
|
6天前
|
Dart 前端开发 开发者
【Flutter前端技术开发专栏】Flutter中的性能分析工具Profiler
【4月更文挑战第30天】Flutter Profiler是用于性能优化的关键工具,提供CPU、GPU、内存和网络分析。它帮助开发者识别性能瓶颈,如CPU过度使用、渲染延迟、内存泄漏和网络效率低。通过实时监控和分析,开发者能优化代码、减少内存占用、改善渲染速度和网络请求,从而提升应用性能和用户体验。定期使用并结合实际场景与其它工具进行综合分析,是实现最佳实践的关键。
【Flutter前端技术开发专栏】Flutter中的性能分析工具Profiler
|
6天前
|
监控 安全 Go
【Go语言专栏】Go语言中的并发性能分析与优化
【4月更文挑战第30天】Go语言以其卓越的并发性能和简洁语法著称,通过goroutines和channels实现并发。并发性能分析旨在解决竞态条件、死锁和资源争用等问题,以提升多核环境下的程序效率。使用pprof等工具可检测性能瓶颈,优化策略包括减少锁范围、使用无锁数据结构、控制goroutines数量、应用worker pool和优化channel使用。理解并发模型和合理利用并发原语是编写高效并发代码的关键。
|
6天前
|
监控 Swift 开发者
【Swift开发专栏】Swift中的性能分析工具:Instruments
【4月更文挑战第30天】Apple的Instruments是Xcode中的性能分析神器,支持Swift和Objective-C,用于识别和解决Mac/iOS应用的性能问题。它提供实时监控、多合一模板、交互式界面和详细报告。通过启动Instruments、选择分析模板、配置选项、开始/停止分析及查看结果,开发者能定位性能瓶颈。优化技巧包括减少CPU负载、优化内存、减少磁盘I/O、网络优化、UI响应和并发处理。定期使用Instruments进行性能分析和优化,可提升应用性能和用户体验。
|
20天前
|
缓存 监控 架构师
Linux 性能分析工具汇总
Linux 性能分析工具汇总
31 0

热门文章

最新文章