Skip to content

Nsight Systems(nsys) ​

Nsight Systems 用于观察 CPU 与 GPU 的执行时间线,定位 CUDA API 调用、kernel、通信、同步及空闲区间。本文先介绍 nsys 的命令、采集参数和报告,再给出 SGLang 的固定 batch 与 server 实践。正式延迟和吞吐测量见 SGLang Benchmark。

1. 命令与参数 ​

1.1 基础采集 ​

在安装了 Nsight Systems 的 GPU 机器上,将待分析程序放在 nsys profile 参数之后。以下以 Python 脚本为例,将 your_script.py 替换为实际脚本及其参数:

bash
nsys profile \
  --sample=none \
  --trace=cuda,nvtx \
  --cuda-graph-trace=node \
  --force-overwrite=true \
  -o report \
  python3 your_script.py

程序正常退出后生成 report.nsys-rep。该命令没有限定 capture range,会包含启动、初始化和正式计算;需要排除初始化时,再使用应用提供的 cudaProfilerStart/Stop 控制采集边界。

1.2 常用参数 ​

参数作用
--sample=none关闭 CPU 指令指针与调用栈采样,保留 CUDA trace。
--trace=cuda,nvtx记录 CUDA API、GPU 活动和程序已有的 NVTX 标记。
--cuda-graph-trace=node展开 CUDA Graph 内部节点,便于观察 kernel;有额外采集开销。
--cuda-graph-trace=graph按整个 graph 记录,开销较低,但不展开内部节点。
--capture-range=cudaProfilerApi等待应用显式调用 cudaProfilerStart() 才开始采集。
--capture-range-end=stop第一次范围结束时停止采集,应用继续运行,后续范围被忽略。
--capture-range-end=none忽略范围结束信号;短任务可一直采集到进程退出。
--capture-range-end=repeat允许多次 start/stop,每段形成独立采集,适合长期服务。
--force-overwrite=true允许覆盖同名报告;重复范围模式的文件命名以 Nsight 输出为准。
-o report指定输出报告路径前缀。

--capture-range 决定何时开始,--capture-range-end 决定范围结束后的行为。仅配置 cudaProfilerApi 而没有应用端触发,不会自动采集正式计算。none 适合会自行退出的短任务;持续运行的 server 应使用 stop 或 repeat,并显式结束采集。

3. SGLang 固定 Batch 实践 ​

场景入口适用范围
固定 BS 的模型执行分析,日常推荐sglang.bench_one_batch精确控制一次 prefill 和 decode 步数,不经过 HTTP 调度。
真实服务的调度与执行分析sglang.launch_server + sglang.bench_one_batch_server一组请求经过 server 调度,实际 GPU batch 可能变化。

以下示例适用于普通自回归解码,不开启 speculative decoding。Prefill 生成首 token,10 步 decode 再生成 10 个 token,所以总输出长度是 11。 “BS=32”指 32 条序列;prefill 每条输入 INPUT_LEN 个 token,decode 每步每条处理 1 个新增 token。

3.1 环境配置 ​

在安装了 SGLang 和 nsys 的 GPU 机器上使用 Bash 执行,先停止占用同组 GPU 的 server:

bash
export MODEL_PATH=/path/to/model
export CUDA_VISIBLE_DEVICES=0
export TP_SIZE=1
export INPUT_LEN=1024
export DECODE_STEPS=10
export PROFILE_DIR=profiles/sglang_run1
mkdir -p "$PROFILE_DIR"

替换模型路径,并按可见 GPU 数量设置 TP。调整 INPUT_LEN 即可改变 prefill 长度;保持同一长度比较不同 BS。确认输入加输出长度符合模型上下文限制,并有足够 KV Cache 容量。

3.2 单个 Batch ​

定义一次函数,再指定 BS。第二个参数控制从哪个阶段开始采集:默认 prefill 包含 prefill 和后续 decode,decode 则跳过 prefill 的采集。

bash
profile_batch() {
  local BS="$1"
  local START_STAGE="${2:-prefill}"
  local OUTPUT_LEN=$((DECODE_STEPS + 1))
  local PREFIX="${PROFILE_DIR}/bs${BS}_in${INPUT_LEN}_from_${START_STAGE}_dec${DECODE_STEPS}"

  nsys profile \
    --sample=none \
    --trace=cuda,nvtx \
    --cuda-graph-trace=node \
    --capture-range=cudaProfilerApi \
    --capture-range-end=none \
    --force-overwrite=true \
    -o "$PREFIX" \
    python3 -m sglang.bench_one_batch \
      --model-path "$MODEL_PATH" \
      --tp-size "$TP_SIZE" \
      --mem-fraction-static 0.7 \
      --batch-size "$BS" \
      --input-len "$INPUT_LEN" \
      --output-len "$OUTPUT_LEN" \
      --profile \
      --profile-activities CUDA_PROFILER \
      --profile-stage "$START_STAGE" \
      --profile-start-step 0 \
      --run-name "bs${BS}_in${INPUT_LEN}_from_${START_STAGE}_dec${DECODE_STEPS}" \
      --result-filename "${PREFIX}.jsonl"
}

# BS=1,每条输入 1024 tokens,采集 prefill + 10 步 decode
INPUT_LEN=1024 profile_batch 1

# BS=64,每条输入 8192 tokens,只采集随后的 10 步 decode
INPUT_LEN=8192 profile_batch 64 decode

两种命令择需运行,分别生成 bs1_in1024_from_prefill_dec10.nsys-rep 和 bs64_in8192_from_decode_dec10.nsys-rep,位于 PROFILE_DIR 下。相同配置再次执行会覆盖报告,独立实验应修改 PROFILE_DIR;JSONL 计时结果则追加写入。

初始化和工具预热均发生在采集开始之前。两种模式的区别是采集起点:

  • --profile-stage prefill:在正式 prefill 前触发 cudaProfilerStart()。--capture-range-end=none 忽略 prefill 后的停止信号,继续记录后续 10 步 decode,直到测试进程退出。
  • --profile-stage decode --profile-start-step 0:先执行真实 prefill 建立 KV Cache,在第 1 步 decode 前触发采集。0 是从零开始的 decode 步编号;不指定它时,本地实现默认从 output_len // 2 开始,会漏掉前面的 decode。

--output-len 11 保证正式测试只执行 10 步 decode;--capture-range-end=none 持续记录到进程退出,因此两种报告尾部都会包含结果处理和退出清理。分析时选中模型执行区间,不把整份报告总时长当成推理延迟。包含 prefill 的模式不能改成 --capture-range-end=stop,否则会在 prefill 后结束,漏掉 decode。

BS=64、INPUT_LEN=8192 对应 64 条各含 8192 tokens 的序列,prefill 合计处理 524288 个输入 tokens。只抓 decode 会减小报告,但不会省掉 prefill 计算和 KV Cache 显存;容量不足时应调整 TP、BS 或长度,并检查工具是否跳过该配置。

4. SGLang Server 实践 ​

4.1 服务启动 ​

需要观察实际 scheduler、overlap 或多请求行为时使用 server 模式。终端 A 先执行第 3.1 节环境配置,再启动:

bash
export PORT=30000
export GRAPH_BS_FLAG=--cuda-graph-max-bs-decode

nsys profile \
  --sample=none \
  --trace=cuda,nvtx \
  --cuda-graph-trace=node \
  --capture-range=cudaProfilerApi \
  --capture-range-end=repeat \
  -o "${PROFILE_DIR}/server" \
  python3 -m sglang.launch_server \
    --model-path "$MODEL_PATH" \
    --host 0.0.0.0 --port "$PORT" \
    --tp-size "$TP_SIZE" \
    --chunked-prefill-size -1 \
    --disable-radix-cache \
    --max-running-requests 128 \
    "$GRAPH_BS_FLAG" 128 \
    --mem-fraction-static 0.7

旧版若只有 --cuda-graph-max-bs,将 GRAPH_BS_FLAG 改成该名称。两个 128 分别是运行请求数和 decode graph 捕获的上限,并不固定 GPU batch;测试更大的 BS 时需要同步调整。关闭 radix cache 是为了避免预热命中前缀缓存,关闭 chunked prefill 是为了便于观察完整输入处理。

4.2 批请求采集 ​

服务 ready 后,终端 B 同样设置第 3.1 节变量和 PORT=30000。以下函数向已有服务提交一组固定长度的请求,默认忽略 EOS,输出 11 个 token。每档先执行一次不采集的预热,再采集同样的一组请求:

bash
export PORT=30000
BS_LIST=(1 8 32 64 128)

run_batch() {
  python3 -m sglang.bench_one_batch_server \
    --model-path "$MODEL_PATH" \
    --base-url "http://127.0.0.1:${PORT}" \
    --batch-size "$1" \
    --input-len "$INPUT_LEN" \
    --output-len "$((DECODE_STEPS + 1))" \
    --skip-warmup \
    --result-filename "${PROFILE_DIR}/server_batch.jsonl"
}

for BS in "${BS_LIST[@]}"; do
  echo "Profiling BS=${BS}, input=${INPUT_LEN}, decode=${DECODE_STEPS}"
  run_batch "$BS" || break

  curl --fail --silent --show-error \
    -X POST "http://127.0.0.1:${PORT}/start_profile" \
    -H 'Content-Type: application/json' \
    -d '{"activities":["CUDA_PROFILER"]}' || break

  run_batch "$BS"

  curl --fail --silent --show-error \
    -X POST "http://127.0.0.1:${PORT}/stop_profile" || break
done

只测一个 BS 时将列表改成 BS_LIST=(32)。这里由 curl 控制 Nsight,不给客户端加 --profile,避免引入 Torch Profiler 或另一套按 step 截断的逻辑。当前源码中 bench_one_batch_server --profile-activities 不接受 CUDA_PROFILER,因此使用显式服务接口更直接。

--base-url 连接已有服务,--skip-warmup 关闭客户端自带预热;循环已经在采集前以相同长度手动预热。JSONL 文件包含预热和采集两次结果,它仅作运行记录,正式 benchmark 使用 Benchmark 中不带 profiler 的流程。

Server 中的 BS 是提交的请求组大小,不能保证只执行一个同样大小的 GPU prefill batch。 即使关闭 chunked prefill,token 预算、KV Cache 容量和调度仍可能分批接纳请求。每条请求输出 11 个 token,不等于整台 server 总共只运行 11 次 forward;要求固定一个 prefill batch 和 10 个 decode batch 时使用第 3 节。

repeat 允许多次 start/stop,每段对应一份报告。报告按采集顺序与 BS_LIST 对应,以 Nsight 日志中的路径为准。全部完成后在终端 A 按一次 Ctrl+C,等待报告导出和进程正常退出;不要用 kill -9。此流程应在没有其他请求流量时执行,否则其他请求也会进入采集窗口。

5. 参考 ​

  1. NVIDIA Nsight Systems User Guide
  2. SGLang:One Batch Benchmark
  3. SGLang:One Batch Server Benchmark
  4. SGLang:Benchmark and Profiling