跳到主要内容
推理优化

9.3 性能分析工具:torch.profiler、Nsight Systems 与 Nsight Compute

推理场景性能分析方法论:torch.profiler 算子级分析、Nsight Systems 全链路分析、Nsight Compute Kernel 级下钻、vLLM 内置 profiling

torch.profilerNsight SystemsNsight ComputeProfilingKernel

压测告诉你”慢”,分析工具告诉你”为什么慢”。9.3 讲三层分析工具:torch.profiler(算子级,快上手)、Nsight Systems(全链路时间线,看调度与空闲)、Nsight Compute(kernel 内部,看算力/带宽利用)。方法论核心:从粗到细逐层下钻,先定位到阶段,再定位到算子,最后定位到 kernel 内部。

📑 目录


1. 三层分析模型:从阶段到 Kernel

第 1 层:阶段级(Nsight Systems / vLLM 日志)
  请求在 Prefill / Decode / 调度 / 网络 / 排队 各花多少时间?
  → 定位"慢在哪个阶段"

第 2 层:算子级(torch.profiler / Nsight Systems kernel view)
  阶段内的哪些算子(QKV GEMM、Attention、FFN、采样)耗时最多?
  → 定位"哪个算子"

第 3 层:Kernel 内部(Nsight Compute)
  算子的 GPU kernel 是否吃满?瓶颈是算力、带宽、延迟还是 launch 开销?
  → 定位"为什么慢"

📌 关键点永远从第 1 层开始。推理优化的常见错误是跳过阶段分析直接下钻 kernel——“Attention kernel 慢”的结论如果放到整体看只是 2% 的时间占比,就是白忙。先找大头,再钻细节


2. torch.profiler:算子级快速定位

2.1 基本用法

import torch

with torch.profiler.profile(
    activities=[
        torch.profiler.ProfilerActivity.CPU,
        torch.profiler.ProfilerActivity.CUDA,
    ],
    schedule=torch.profiler.schedule(wait=1, warmup=2, active=5),
    on_trace_ready=torch.profiler.tensorboard_trace_handler("./trace"),
) as prof:
    for step in range(8):
        # 推理一 batch(decode 步)
        output = model.generate(...)
        prof.step()

# 命令行/Notebook 查看:chrome://tracing 或 tensorboard
prof.key_averages().table(sort_by="cuda_time_total", row_limit=30)

2.2 解读关键字段

字段含义
Self CUDA Time该算子自身的 GPU 时间(排除子操作)——先看这个
CUDA Time Total含子操作的总时间
CPU TotalCPU 侧耗时(launch 开销、数据搬运)
CUDA Mem显存分配量
Call Stack调用栈(定位到代码行)

2.3 推理场景的看表顺序

  1. 按 Self CUDA Time 排序,看前 5 的算子(GEMM/Attention/采样/量化反量化?)
  2. 算占比:top1 占 prefill/decode 总时间多少?(>50% 才值得深挖)
  3. 看 CPU 时间 vs CUDA 时间:CPU > CUDA 说明 launch-bound(kernel 启动开销掩盖计算)——小 batch 场景常见
  4. 看显存分配热点torch.empty/copy 频繁 → 碎片与搬运问题

💡 提示:torch.profiler 的输出可以直接在 TensorBoardtensorboard --logdir ./trace)里交互看时间线,支持按算子过滤和 GPU 利用视图——比 chrome://tracing 更适合深挖。


3. Nsight Systems:全链路时间线

3.1 是什么

NVIDIA 系统级 profiler:抓 CPU/GPU 时间线 + CUDA API + 内核活动 + 内存拷贝 + 通信(NCCL)。不深入 kernel 内部,但能看到”谁在等谁”。

nsys profile -o trace_out --trace=cuda,nvtx,osrt \
  vllm serve meta-llama/Llama-3.1-8B-Instruct
# 或对已运行进程 attach
nsys profile --attach-pid <pid> -o trace_out

3.2 推理场景最值钱的三个视图

视图看什么典型结论
Timeline(默认)GPU 空闲段与 kernel 间隙“GPU 在等 CPU 喂数据”(launch-bound)
CUDA API 视图API 调用时长与重叠cudaMemcpy 串行搬运
NCCL 视图通信与计算是否重叠TP 场景通信间隙(6.2 的通信账)

3.3 推理场景的典型发现

GPU 时间线长段空白 + CPU 忙
→ launch-bound:kernel 太小/太多,CPU 调度不过来
   (对策:算子融合、加大 batch、CUDA Graph)

kernel 之间规则性小空隙
→ 通信或同步点:TP 的 allreduce、PD 分离的传输
   (对策:通信计算重叠、减少同步)

prefill 与 decode 的 GPU 片段交织
→ 两者互抢算力(未分离架构)
   (对策:第 7 章的 PD 分离)

📌 关键点:Nsight Systems 回答的是”时间去哪了”(调度、等待、搬运、通信),Nsight Compute 回答”kernel 本身为什么慢”。推理排障先 Nsight Systems——大多数推理性能问题出在”没在算”而不是”算得慢”


4. Nsight Compute:Kernel 内部下钻

4.1 是什么

Kernel 级 profiler:分析单个 CUDA kernel 的 SM 占用、内存带宽、指令吞吐、warp 停滞原因。

# 只 profile 指定 kernel(避免海量输出)
ncu --kernel-name regex:.*flash_attn.* --launch-count 3 -o kern_out \
  <推理命>
# 或对运行中进程
ncu --target-processes all -o kern_out --kernel-name-base demangled <cmd>

4.2 关键指标

指标含义判断
Compute (SM) ThroughputSM 算力利用率>80% = 算力饱和
Memory Throughput显存带宽利用率>80% = 带宽饱和
Achieved Occupancy实际 SM 占用率低 → 并发度不足
Warp Stall Reasonswarp 等待原因Long Scoreboard(等显存)/ Barrier(同步)
Launch Statistics启动开销小 kernel 的启动占比

4.3 推理场景的四个典型诊断

带宽饱和 + 算力低 → 访存密集型(decode 的 KV 读取、量化模型的权重读取)
算力饱和 + 带宽低 → 计算密集型(大 batch GEMM)
两者都低 + occupancy 低 → 并发不足(batch 小、序列短、wave 不满)
两者都低 + stall 高 → 延迟受限(依赖链、小 GEMM 的 tail effect)

📌 关键点decode 阶段几乎总是带宽瓶颈(每 token 读全部权重,计算量小);prefill 大 batch 时是算力瓶颈。Ncu 数据要配合这个先验判断——“decode 的 GEMM 带宽 90%“是正常的,重点看是不是异常地算力受限(如量化精度导致的非对齐访问)。


5. vLLM 内置 Profiling 与日志

5.1 服务端 profiler 配置

vllm serve <model> \
  --profiler-config '{"start_profiler_on_next_step": true, "num_steps_to_profile": 5, "output_dir": "./profiles"}'

配合压测端 vllm bench serve --profile(9.2)在真实负载下抓 trace。

5.2 关键日志指标(无需工具先看这些)

# 启动日志:显存分布(权重 / KV 池 / 激活)
Memory profiling results:  total_gpu_memory=80.0GiB ...
                         weights=16.1GiB, kv_cache=60.0GiB, ...

# 请求日志:调度信息
INFO: Received request ... prompt=120 tokens, sampling_params=...
INFO: Finished request ... generated=200 tokens, TTFT=..., TPOT=...
  • KV 池大小:决定并发上限(4.x 的 gpu_memory_utilization 调这里)
  • 每请求 TTFT/TPOT:不用起任何工具就能看真实负载的分布——先看日志,再上 profiler

💡 提示:把 vLLM 的 /metrics(Prometheus 格式)接入 Grafana:vllm:num_requests_runningvllm:gpu_cache_usage_percvllm:time_to_first_token_seconds 等——日常监控用 metrics,深度排障用 profiler,两者分工。


6. 推理场景的典型瓶颈图谱

┌─────────────────────────────────────────────────────────┐
│ 现象                       │ 优先检查                    │
├─────────────────────────────────────────────────────────┤
│ TTFT 高(prefill 慢)       │ batch 太大?chunked prefill?│
│                             │ 长 prompt?APC 命中率?      │
│ TPOT 高(decode 慢)        │ 带宽饱和?batch 过大?       │
│                             │ 量化精度?                  │
│ 吞吐低但延迟正常            │ 调度器排队?max-num-seqs 小? │
│ GPU 有空闲但利用率高        │ launch-bound(小 kernel)    │
│ 多卡扩展性差                │ NCCL 通信未重叠?拓扑不对?   │
│ 显存峰值突增                │ 激活值峰值?KV 碎片?         │
└─────────────────────────────────────────────────────────┘

📌 关键点:瓶颈图谱的每一行都对应前面章节的内容——第 2 章(APC/调度)、4.x(量化/显存)、5.x(投机解码)、6.x(并行/通信)、7.x(PD 分离)。性能分析不是孤立技巧,是把全书知识串起来的诊断过程。


7. 分析实战流程:一次完整的性能排障

假设现象:并发 64 时 TPOT P99 从 30ms 涨到 90ms

第 1 步(阶段级,Nsight Systems / metrics)
  看 GPU 时间线:prefill 与 decode 交织?
  → 发现 decode 步频繁被 prefill 插入打断(未分离架构)

第 2 步(算子级,torch.profiler)
  decode 步算子排序:Self CUDA Time top3
  → QKV GEMM 38%、FlashAttention 22%、FFN 30%

第 3 步(Kernel 级,Nsight Compute)
  对 QKV GEMM 下钻:Memory Throughput 85%(带宽饱和)
  → 确认:decode 是带宽瓶颈,batch 变大后每 token 权重读取翻倍

结论与对策
  带宽受限 + 并发上升 → decode 算力被 prefill 抢占 + 带宽饱和
  → 对策:PD 分离(第 7 章)或限制 prefill 频率(调度策略);
         或对权重做更低精度量化(4.x)降低带宽需求

💡 提示每次排障都写”现象 → 证据 → 结论 → 对策 → 验证”五段式记录。证据(trace 截图、指标数值)要存档——这是 9.5 回归门禁的退化定位素材库。


📝 总结

  • 三层下钻:阶段(Nsight Systems)→ 算子(torch.profiler)→ kernel(Nsight Compute);先找大头
  • torch.profiler:Self CUDA Time 排序;CPU>CUDA = launch-bound
  • Nsight Systems:回答”时间去哪了”——调度、等待、搬运、通信
  • Nsight Compute:回答”kernel 为什么慢”——算力/带宽/占用/停滞
  • decode 几乎总是带宽瓶颈,prefill 大 batch 是算力瓶颈
  • vLLM 自带:profiler 配置 + 日志 TTFT/TPOT + /metrics Prometheus
  • 瓶颈图谱:每行现象对应全书某章知识——分析是知识的诊断应用

🎯 自我检验清单

  • 三层分析模型各自回答什么问题?为什么先做阶段级?
  • torch.profiler 的看表顺序?(排序→占比→CPU/CUDA→显存)
  • launch-bound 的特征与对策?
  • Nsight Systems 三个视图各看什么?典型发现?
  • Nsight Compute 四个典型诊断组合(算力/带宽/占用/停滞)?
  • 为什么 decode 是带宽瓶颈?(每 token 读全部权重)
  • 用五段式(现象→证据→结论→对策→验证)复述 7 的案例?

📚 参考资料