Go 语言基础(22) | 青训营笔记

79 阅读2分钟

今天没有代码,需要捋一捋 pppof 采样过程的笔记,便于之后灵活运用,因为可统计的指标有很多。

pprof 采样过程和原理

CPU

对于 CPU 上面的性能讨论,主要是函数调用和其占用时间。采样率设置为每秒 100 次,固定死,然后手动启动采样,一段时间以后手动结束。

手动开始的流程大概是:

flowchart LR
    开始采样 --> 设定信号处理函数 --> 开启定时器

手动结束刚好相反:

flowchart LR
    停止采样 --> 取消信号处理函数 --> 关闭定时器

采样时(进程开启定时器和关闭定时器之间)操作系统每 10 ms 向进程发送一次 SIGPROF 信号,进程每次接收到 SIGPROF 就记录调用堆栈,写缓冲每 100 ms 读取已经记录的调用栈并写入输出流。

Heap 堆内存

性能指标不止一种,采样程序通过内存分配器在堆上分配和释放的内存,记录分配或释放的大小和数量。采样率设置为每分配 512 KB 记录一次,可以在运行开头修改,设置上 1 表示每次分配都记录。采样时间的计算是从程序运行开始到采样时。采样的指标有 alloc_spacealloc_objectsinuse_spaceinuse_objects。计算方式为 inuse_ = alloc_ - free_

Goroutine 协程和 ThreadCreate 线程创建

这是另一套指标。对于 Goroutine:记录所有用户发起且在运行中的 goroutine(函数入口非 runtime 开头的)runtime.main 的调用栈信息

flowchart LR
    n1[Stop The World] --> n2[遍历 allg 切片] --> n3[输出创建 g 的堆栈] --> n4[Start The World]

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

flowchart LR
    n1[Stop The World] --> n2[遍历 allm 切片] --> n3[输出创建 m 的堆栈] --> n4[Start The World]

Block 阻塞和 Mutex 锁

对于阻塞操作,采样阻塞操作的次数和耗时,采样率设置为阻塞耗时超过阈值记录一次,设置上 1 表示每次阻塞均记录。

flowchart TD
    n1[阻塞操作] -- 上报调用栈和消耗时间 --> n2[Profiler] -- 采样 --> n4[遍历阻塞记录] --> n5[统计阻塞次数和耗时]
    n2 --> n3[时间未到阈值则丢弃]

对于锁竞争,采样争抢锁的次数和耗时,采样率设置为只记录固定比例的锁操作,设置上 1 表示每次加锁均记录。

flowchart TD
    n1[锁竞争操作] -- 上报调用栈和消耗时间 --> n2[Profiler] -- 采样 --> n4[遍历锁记录] --> n5[统计锁竞争次数和耗时]
    n2 --> n3[比例未命中则丢弃]