首页 > 编程语言 >Go语言精进之路读书笔记第47条——使用pprof对程序进行性能剖析

Go语言精进之路读书笔记第47条——使用pprof对程序进行性能剖析

时间:2024-03-10 20:24:14浏览次数:24  
标签:profile err 读书笔记 pprof 47 test go os

47.1 pprof的工作原理

1.采样数据类型

(1) CPU数据
(2) 堆内存分配数据
(3) 锁竞争数据
(4) 阻塞时间数据

2.性能数据采集的方式

(1) 通过性能基准测试进行数据采集

go test -bench . xxx_test.go -cpuprofile=cpu.prof
go test -bench . xxx_test.go -memprofile=mem.prof
go test -bench . xxx_test.go -mutexprofile=mutex.prof
go test -bench . xxx_test.go -blockprofile=block.prof

(2) 独立程序的性能数据采集

  • 可以通过标准库runtime/pprof和runtime包提供的低级API对独立程序进行性能数据采集。
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
var memprofile = flag.String("memprofile", "", "write memory profile to `file`")
var mutexprofile = flag.String("mutexprofile", "", "write mutex profile to `file`")
var blockprofile = flag.String("blockprofile", "", "write block profile to `file`")

func main() {
    flag.Parse()
    if *cpuprofile != "" {
        f, err := os.Create(*cpuprofile)
        if err != nil {
            log.Fatal("could not create CPU profile: ", err)
        }
        defer f.Close() // 该例子中暂忽略错误处理
        if err := pprof.StartCPUProfile(f); err != nil {
            log.Fatal("could not start CPU profile: ", err)
        }
        defer pprof.StopCPUProfile()
    }

    if *memprofile != "" {
        f, err := os.Create(*memprofile)
        if err != nil {
            log.Fatal("could not create memory profile: ", err)
        }
        defer f.Close()
        if err := pprof.WriteHeapProfile(f); err != nil {
            log.Fatal("could not write memory profile: ", err)
        }
    }

    if *mutexprofile != "" {
        runtime.SetMutexProfileFraction(1)
        defer runtime.SetMutexProfileFraction(0)
        f, err := os.Create(*mutexprofile)
        if err != nil {
            log.Fatal("could not create mutex profile: ", err)
        }
        defer f.Close()

        if mp := pprof.Lookup("mutex"); mp != nil {
            mp.WriteTo(f, 0)
        }
    }

    if *blockprofile != "" {
        runtime.SetBlockProfileRate(1)
        defer runtime.SetBlockProfileRate(0)
        f, err := os.Create(*blockprofile)
        if err != nil {
            log.Fatal("could not create block profile: ", err)
        }
        defer f.Close()

        if mp := pprof.Lookup("mutex"); mp != nil {
            mp.WriteTo(f, 0)
        }
    }

    var wg sync.WaitGroup
    c := make(chan os.Signal, 1)
    signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
    wg.Add(1)
    go func() {
        for {
            select {
            case <-c:
                wg.Done()
                return
            default:
                s1 := "hello,"
                s2 := "gopher"
                s3 := "!"
                _ = s1 + s2 + s3
            }

            time.Sleep(10 * time.Millisecond)
        }
    }()
    wg.Wait()
    fmt.Println("program exit")
}
  • Go在net/http/pprof包中还提供了一种更为高级的针对独立程序性能数据采集方式,尤其适合那些内置了HTTP服务的独立程序。
import (
    "context"
    "fmt"
    "net/http"
    _ "net/http/pprof"
    "os"
    "os/signal"
    "syscall"
)

func main() {
    http.Handle("/hello", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Println(*r)
        w.Write([]byte("hello"))
    }))
    s := http.Server{
        Addr: "localhost:8080",
    }
    c := make(chan os.Signal, 1)
    signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
    go func() {
        <-c
        s.Shutdown(context.Background())
    }()
    fmt.Println(s.ListenAndServe())
}

3.性能数据的剖析

(1) 命令行交互方式

  • flat列的值表示函数自身代码在数据采样过程中的执行时长,flat%列的值表示该执行时长占总采样执行时长的百分比
  • sum%列的值是当前行flat%值与排在该值前面所有行的flat%值的累加和
  • cum列的值表示函数自身在数据采样过程中出现的时长,这个时长是其自身代码执行时长及其等待其调用的函数返回所用时长的总和。越是接近函数调用栈底层的代码,其cum列的值越大
  • cum%列的值表示该函数cum值占总采样时长的百分比

(2) Web图形化方式

  • Top视图等价于命令行交互模式下的topN命令输出
  • Source视图等价于命令行交互模式下的list命令输出
  • Flame Graph视图即火焰图
    • go tool pprof在浏览器中呈现出的火焰图与标准火焰图有些差异:它是倒置的,即调用栈最顶端的函数在最下方
    • 倒置火焰图就是看最下面的哪个函数占据的宽度最大,这样的函数可能存在性能问题

47.2 使用pprof进行性能剖析的实例

1.待优化程序(step0)

2.CPU类性能数据采样及数据剖析(step1)

3.第一次优化(step2)
优化手段:正则表达式仅编译一次

4.内存分配采样数据剖析

5.第二次优化(step3)

  • 删除w.Header().Set这行调用
  • 使用fmt.Fprintf替代w.Write

6.零内存分配(step4)

  • 使用sync.Pool减少重新分配bytes.Buffer次数
  • 采用预分配底层存储的bytes.Buffer拼接输出
  • 使用strconv.AppendInt将整型数拼接到bytes.Buffer中

7.查看并发下的阻塞情况(step5)

  • 无需处理,Go标准库对regexp包的Regexp.MatchString方法做过针对并发的优化(也是采用sync.Pool)

标签:profile,err,读书笔记,pprof,47,test,go,os
From: https://www.cnblogs.com/brynchen/p/18064722

相关文章

  • Go语言精进之路读书笔记第49条——使用Delve调试Go代码
    49.1关于调试,你首先应该知道的几件事1.调试前,首先做好心理准备2.预防bug的发生,降低bug的发生概率(1)充分的代码检查(2)为调试版添加断言(3)充分的单元测试(4)代码同级评审3.bug的原因定位和修正(1)收集“现场数据”(2)定位问题所在(3)修正并验证49.2Go调试工......
  • P4147 玉蟾宫
    原题链接题解太巧妙了把每个点上方的连续f长度记录下来,然后求每行的柱状图构成的矩形的最大面积code#include<bits/stdc++.h>usingnamespacestd;intf[1005][1005]={0};intn,m;structnode{inth,cnt;};intsolve(introw)//每一行列上的高度,如果全部使用,......
  • 47. 全排列 IIc
    我宣布我已经参透了套路,一次AC/***Returnanarrayofarraysofsize*returnSize.*Thesizesofthearraysarereturnedas*returnColumnSizesarray.*Note:Bothreturnedarrayand*columnSizesarraymustbemalloced,assumecallercallsfree().*/int......
  • P4774 屠龙勇士 题解
    传送门显然每一只龙对应了唯一的一把剑。用multiset可以求出每一把剑。于是题目就变成了:\[\begin{cases}b_1x\equiva_1\pmod{m_1}\\b_2x\equiva_2\pmod{m_2}\\\dots\\b_nx\equiva_n\pmod{m_n}\end{cases}\]如果\(b_i=1\),直接EXCRT即可。现在\(b_i>1\),还是以EXCRT......
  • P4765 CERC2014 The Imp
    设我们打算买的\(K+1\)个物品为\(p_1,p_2,\cdots,p_{K+1}\)。则收益为\(min(v_{p_1}-c_{p_1},v_{p_2}-c_{p_1}-c_{p_2},\cdots,v_{p_{K+1}}-\sum_{i=1}^{K+1}c_{p_i})\)。从邻项交换的角度,考虑我们按哪种顺序购买这\(K+1\)个物品收益最大。任取相邻两项\((v_{p_x},c_{p_......
  • 读书笔记(2)《微精通》
    读书笔记(2)《微精通》背景:在学习如何有效读书的方法之后,想要更进一步的学习了解高效掌握一门技艺的方法。是的,还是带着无比功利性的目的挑选了这本书,希望能帮助自己在将来这段时间内充分利用全部精力和时间来稳步提升自我‍‍内容摘录:发觉兴趣+识别重心+立体学习+......
  • [HDU6647] Bracket Sequences on Tree 题解
    [HDU6647]BracketSequencesonTree题解一道纯靠自己推出来的换根\(dp+\)树哈希,写篇题解庆祝一下~~题意:给定一棵无根树,你可以任意选择根节点和遍历顺序,每次遍历时进入一个节点就标记一个(,离开一个节点就标记一个),问所有存在的括号序列有多少种,对998244353取模。先考虑根固......
  • 万字长文讲解Golang pprof 的使用
    往期好文推荐⭐️⭐️⭐️:#golangpprof监控系列(1)——gotrace统计原理与使用#golangpprof监控系列(2)——memory,block,mutex使用#golangpprof监控系列(3)——memory,block,mutex统计原理#golangpprof监控系列(4)——goroutinethread统计原理#golangpprof......
  • CF147B 题解
    Solution一道十分典型的dp题。有三个关键点分别是定义状态、优化和答案的统计。首先定义状态,定义\(f_{i,j,p}\)表示\(i\toj\)号节点,共走了不超过\(p\)条边,且是\(i\toj\)的最长路径。不超过\(p\)条边是为了方便转移,而最长路径如果都为负环,说明需要走更多的边,实......
  • solution-at4703
    题解AT4703【RedorBlue】原题来介绍一下三元运算符:A?B:C如果表达式A为真,则执行B语句,否则执行C语句。其作用就相当于:if(A){ B;}else{ C;}例如1+1>2?puts("IAKIOI"):puts("qwq");将会输出qwq。那么此题代码就可以变得极简。代码#include<iostr......