文档目录

一、三步生成火焰图

# 第一步:采样
perf record -F 99 -g ./my_program

# 第二步:转换为火焰图工具需要的格式
perf script > out.perf

# 第三步:生成 SVG 火焰图
git clone https://github.com/brendangregg/FlameGraph
FlameGraph/stackcollapse-perf.pl out.perf > out.folded
FlameGraph/flamegraph.pl out.folded > flame.svg

# 用浏览器打开 flame.svg

二、火焰图解读方法

火焰图的阅读方向是"自底向上":
  底部:调用链的起点(main、线程入口函数)
  顶部:CPU 正在执行的函数

x 轴:采样数量(不是时间)
  → 一个函数条越宽,它占的 CPU 时间越多

y 轴:调用栈深度
  → 顶层函数是被调用的,底层是调用者

颜色:没有特殊含义,随机分配(但也有风格按函数名 hash 上色)

平顶(顶部宽,底部窄):
  这个函数本身的计算量大
  ┌───────────────────┐
  │  compute_dct       │ ← 宽 → 大部分时间花在 compute_dct 里面
  └───────────────────┘
       ↑ process_frame
       ↑ main

尖顶(顶部窄,底部宽):
  大部分时间花在子函数中,当前函数只是调用了别人
  ┌──┐
  │f1│ ← 窄 → f1 本身不花时间,它调用的子函数花时间
  └──┘
   ↑ f2
   ↑ f3

三、结合自己的项目的解读示例

假设你的 SimpleAsyncLogger 生成火焰图如下:

底部(main):
  main  →  AsyncWriter::run  →  io_uring_enter  ← 宽
                              →  memcpy           ← 窄
                              →  TcpClient::send  ← 中等

分析:
  io_uring_enter 很宽 → 大部分时间花在等待 I/O 完成(正常,异步日志就是等 I/O)
  memcpy 很窄 → 你的 SPSC 零拷贝设计是有效的
  TcpClient::send 中等 → 网络可能成为瓶颈,考虑更大的发送缓冲区

如果 memcpy 很宽 → 说明 SPSC 的零拷贝没有生效,检查是不是走了拷贝路径。
如果 TcpClient::send 很宽 → 网络带宽或 buffer 需要调优。