[Profiling] Explain Kimi-K3 Deep PP scaling
This commit is contained in:
parent
0433fcc3ee
commit
a58b931cc8
@ -263,22 +263,84 @@ A2A 后启动或 Prefill OOM 的情况。
|
||||
|
||||
## 9. 通信结果如何解释
|
||||
|
||||
本轮 PP 搜索没有为 PP2/4/8/16 再录制 Nsight,也没有在每个 benchmark 前后采集
|
||||
独立 HCA Counter,因此不能把 PP1 的 57.27% NCCL 占比直接写成 PP8 的实测
|
||||
通信占比。
|
||||
### 9.1 累计收益变大,但每次增加 PP 的边际收益在下降
|
||||
|
||||
能够被当前证据直接证明的是:
|
||||
第 7 节相对 PP1 的累计数字会让人感觉“PP 越大,收益增长越快”。逐级比较后,
|
||||
实际规律相反。以 C=8 为例:
|
||||
|
||||
1. PP1 的同口径 Nsight 确认 TP32 Prefill 以暴露的 AllReduce 为主;
|
||||
2. PP2/4/8/16 的 `/get_server_info` 证明实际 PP/TP/EP 与计划一致;
|
||||
3. Rank 布局使 PP4 的 TP8、PP8 的 TP4/EP4 都限制在单节点内;
|
||||
4. 所有 PP 配置的 NCCL P2P communicator 成功建立并完成全部请求;
|
||||
5. Input TPS 按 PP1 < PP2 < PP4 < PP8 提升,随后 PP16 回落,表明通信域
|
||||
缩小确有收益,但 Deep PP 开销在 PP8 之后成为更大的限制。
|
||||
| 变化 | Input TPS 边际变化 | TTFT p50 边际变化 |
|
||||
|---|---:|---:|
|
||||
| PP1 -> PP2 | +53.96% | -35.65% |
|
||||
| PP2 -> PP4 | +42.65% | -31.48% |
|
||||
| PP4 -> PP8 | +23.12% | -22.05% |
|
||||
| PP8 -> PP16 | -23.93% | +33.18% |
|
||||
|
||||
因此当前结果支持通信域重构这一解释,但没有伪造一个未经采样的“PP8 NCCL
|
||||
占比”。如需进一步拆解 PP8 内部剩余时间,应单独执行短窗口 Nsight,而不是
|
||||
把 Profile 开销混进这组性能基准。
|
||||
所以 PP1 到 PP8 的累计收益持续扩大,但每次把 PP 翻倍带来的新增收益已经逐步
|
||||
减小;PP16 则越过最优点。C=16 也呈现相同趋势。
|
||||
|
||||
### 9.2 节点内 PCIe 与节点间 RoCE 的实测
|
||||
|
||||
在 601--604 上使用 NCCL 2.28.9 + CUDA 13 的 `all_reduce_perf`,分别测量与
|
||||
PP Stage 对应的 TP4、TP8、TP16 和 TP32 通信域。所有测试均为 0 个错误值:
|
||||
|
||||
| 通信域 | 实际拓扑 | 128 MiB 延迟 | 128 MiB busbw | 128/256 MiB 平均 busbw |
|
||||
|---|---|---:|---:|---:|
|
||||
| TP4 | 单节点 GPU0--3,同一 PCIe/NUMA 岛 | 4.93 ms | 40.85 GB/s | 41.00 GB/s |
|
||||
| TP8 | 单节点 8 卡,跨两个 NUMA 岛 | 6.00 ms | 39.13 GB/s | 38.89 GB/s |
|
||||
| TP16 | 两节点 | 6.24 ms | 40.31 GB/s | 40.60 GB/s |
|
||||
| TP32 | 四节点 | 6.59 ms | 39.47 GB/s | 40.08 GB/s |
|
||||
|
||||
大消息带宽都在约 39--41 GB/s,说明 PP 收益不是因为节点间 400G RoCE 的
|
||||
持续带宽远低于节点内 PCIe。真正变化更明显的是 collective 的参与 Rank 数、
|
||||
固定延迟和同步范围:128 MiB AllReduce 从 TP32 的 6.59 ms 降到 TP4 的
|
||||
4.93 ms。
|
||||
|
||||
两个拓扑边界尤其重要:
|
||||
|
||||
- PP2 -> PP4 把每个 Stage 的 TP 域从两节点 TP16 缩到单节点 TP8,逐层
|
||||
AllReduce 不再跨节点;
|
||||
- PP4 -> PP8 把 TP8 缩到 TP4,每个 Stage 可落在同一个 PCIe/NUMA 岛,避免
|
||||
单节点内跨 CPU Root Complex,同时每卡得到更大的 GEMM 分片,减轻 TP32
|
||||
过度切分造成的小矩阵低效率。
|
||||
|
||||
### 9.3 PP1 与 PP8 的 Nsight 对照
|
||||
|
||||
为避免把 Profiler 开销混入第 7 节性能结果,另起同场景短窗口 Nsight:
|
||||
`16K -> 1, C=8, chunk=8K`。以下是稳定 Rank 的中位数:
|
||||
|
||||
| 每个活跃 Rank/Stage 的指标 | PP1 / TP32 | PP8 / TP4 | 变化 |
|
||||
|---|---:|---:|---:|
|
||||
| 普通 AllReduce 调用数 | 557 | 66 | -88.2% |
|
||||
| 普通 AllReduce 累计时间 | 4,160.6 ms | 392.3 ms | -90.6% |
|
||||
| 单次 AllReduce p50 | 7.10 ms | 5.83 ms | -17.9% |
|
||||
| 计算与 NCCL 时间线重叠 | 约 0 | 约 0 | 未形成有效重叠 |
|
||||
|
||||
这里的 557 -> 66 是**单个 Rank/Stage**的 Profile 窗口,不是一个请求在整个
|
||||
模型上的 collective 总数。PP8 把模型层分给 8 个 Stage,一个请求仍要依次经过
|
||||
全部 Stage;它减少的是每个活跃 Rank 在一个 Stage 内负责的层数和同步工作,
|
||||
并让不同请求的 Stage 可以流水并行,而不是凭空删除模型全部通信。
|
||||
|
||||
PP8 Trace 还出现约 3.43 s 的非 AllReduce NCCL 区间,主要对应 Pipeline
|
||||
Send/Recv 及 Stage 等待。该值包含异步 Stage 等待,不能直接当作 3.43 s 的
|
||||
网络传输时间,也不能把 PP8 的 NCCL wall 百分比与 PP1 做等口径比较。可靠的
|
||||
对照是上表中的调用数、普通 AllReduce 累计时间和单次延迟。
|
||||
|
||||
### 9.4 为什么 PP8 最优,PP16 反而退化
|
||||
|
||||
PP1 -> PP8 的收益由四项共同产生:
|
||||
|
||||
1. TP 通信域由 32 Rank 缩到 4 Rank,单次 collective 延迟降低;
|
||||
2. PP4/PP8 的 TP collective 保持在节点内,PP8 进一步保持在同一 PCIe/NUMA
|
||||
岛;
|
||||
3. 每个 Rank 只执行所在 Stage 的层,C=8/16 的不同请求能在多个 Stage 并行,
|
||||
将通信和计算流水化;
|
||||
4. TP32 过度切分的 GEMM 在 TP4 下变成更大的本地矩阵,Kernel 效率更高。
|
||||
|
||||
收益没有继续延伸到 PP16:C=8 时请求数甚至少于 16 个 Stage,流水线无法填满;
|
||||
C=16 也只有一波请求。更多 Stage 边界、P2P Send/Recv、调度和 bubble 超过了
|
||||
TP4 -> TP2 的剩余收益,而且 PP16 还因整除约束从 EP4 改成 EP2。因此当前证据
|
||||
支持的是“PP8 在这个 Prefill 压力点达到通信、计算粒度与流水线开销的平衡”,
|
||||
而不是“PP 越大越快”。
|
||||
|
||||
## 10. 最终判断与后续使用
|
||||
|
||||
@ -371,3 +433,22 @@ PP1 Nsight 通信归因:
|
||||
kimi3_pro6000_sglang_prefill_communication_profile/
|
||||
results/kimi3-prefill-comm-20260820-143749/nsys_analysis.json
|
||||
```
|
||||
|
||||
PP8 Nsight 通信归因与四组 NCCL 微基准:
|
||||
|
||||
```text
|
||||
/data/hzy/sskj/experiments/pro6000/
|
||||
kimi3_pro6000_sglang_prefill_communication_profile/
|
||||
results/kimi3-prefill-comm-pp8-20260821-172717/
|
||||
|
||||
nsys_analysis.json # PP8 各 Rank CUDA/NCCL 汇总
|
||||
nsys_device_metrics.csv # PP8 各设备时间线指标
|
||||
nsys/node0/prefill_node0.nsys-rep # 601 原始 Nsight Trace
|
||||
nsys/node1/prefill_node1.nsys-rep # 602 原始 Nsight Trace
|
||||
nsys/node2/prefill_node2.nsys-rep # 603 原始 Nsight Trace
|
||||
nsys/node3/prefill_node3.nsys-rep # 604 原始 Nsight Trace
|
||||
nccl_tp4_intra_node.log # 单节点同 PCIe/NUMA 岛
|
||||
nccl_tp8_intra_node.log # 单节点跨 NUMA
|
||||
nccl_tp16_2node.log # 两节点
|
||||
nccl_tp32_4node.log # 四节点
|
||||
```
|
||||
|
||||
@ -17,6 +17,9 @@ DIST_PORT="${DIST_PORT:-20000}"
|
||||
PROFILE_STEPS="${PROFILE_STEPS:-3}"
|
||||
CONCURRENCY="${CONCURRENCY:-8}"
|
||||
NUM_PROMPTS="${NUM_PROMPTS:-40}"
|
||||
PP_SIZE="${PP_SIZE:-1}"
|
||||
TP_SIZE="${TP_SIZE:-32}"
|
||||
EP_SIZE="${EP_SIZE:-4}"
|
||||
CONTAINER_PREFIX="kimi3_prefill_comm_profile"
|
||||
SSH_OPTS=(-o BatchMode=yes -o StrictHostKeyChecking=no -o ConnectTimeout=10)
|
||||
|
||||
@ -89,7 +92,7 @@ start_node() {
|
||||
host="${NODE_HOSTS[$rank]}"
|
||||
name="$(container_name "$rank")"
|
||||
output="${RESULT_ROOT}/nsys/node${rank}/prefill_node${rank}"
|
||||
launch="export SGLANG_HOST_IP=174.1.60.$((rank + 1)); exec nsys profile --trace=cuda,nvtx,nccl --nccl-trace=api,group,gpu,coll,kernel-launch --sample=none --cpuctxsw=none --capture-range=cudaProfilerApi --capture-range-end=stop --cuda-graph-trace=node --force-overwrite=true --stats=true --output ${output} python3 -m sglang.launch_server --model-path ${MODEL_PATH} --served-model-name kimi-k3 --tp-size 32 --ep-size 4 --nnodes 4 --node-rank ${rank} --dist-init-addr ${HEAD_HOST}:${DIST_PORT} --trust-remote-code --moe-runner-backend flashinfer_mxfp4 --chunked-prefill-size 8192 --mem-fraction-static 0.88 --cuda-graph-max-bs-decode 16 --mamba-radix-cache-strategy extra_buffer_lazy --disable-radix-cache --dist-timeout 3600 --mamba-full-memory-ratio 0.36 --host 0.0.0.0 --port ${PORT}"
|
||||
launch="export SGLANG_HOST_IP=174.1.60.$((rank + 1)); exec nsys profile --trace=cuda,nvtx,nccl --nccl-trace=api,group,gpu,coll,kernel-launch --sample=none --cpuctxsw=none --capture-range=cudaProfilerApi --capture-range-end=stop --cuda-graph-trace=node --force-overwrite=true --stats=true --output ${output} python3 -m sglang.launch_server --model-path ${MODEL_PATH} --served-model-name kimi-k3 --tp-size ${TP_SIZE} --pp-size ${PP_SIZE} --ep-size ${EP_SIZE} --nnodes 4 --node-rank ${rank} --dist-init-addr ${HEAD_HOST}:${DIST_PORT} --trust-remote-code --moe-runner-backend flashinfer_mxfp4 --chunked-prefill-size 8192 --mem-fraction-static 0.88 --cuda-graph-max-bs-decode 16 --mamba-radix-cache-strategy extra_buffer_lazy --disable-radix-cache --dist-timeout 3600 --mamba-full-memory-ratio 0.36 --host 0.0.0.0 --port ${PORT}"
|
||||
|
||||
local -a command=(
|
||||
docker run -d --name "$name"
|
||||
@ -191,7 +194,7 @@ write_manifest() {
|
||||
printf 'run_id=%s\n' "$RUN_ID"
|
||||
printf 'image=%s\n' "$IMAGE"
|
||||
printf 'model=%s\n' "$MODEL_PATH"
|
||||
printf 'tp=32\nep=4\ndp=1\n'
|
||||
printf 'pp=%s\ntp=%s\nep=%s\ndp=1\n' "$PP_SIZE" "$TP_SIZE" "$EP_SIZE"
|
||||
printf 'moe_runner_backend=flashinfer_mxfp4\nmoe_a2a_backend=none\n'
|
||||
printf 'input_len=16384\noutput_len=1\nconcurrency=%s\n' "$CONCURRENCY"
|
||||
printf 'chunked_prefill_size=8192\nprofile_steps=%s\n' "$PROFILE_STEPS"
|
||||
@ -200,11 +203,19 @@ write_manifest() {
|
||||
|
||||
run_all() {
|
||||
require_password
|
||||
(( PP_SIZE * TP_SIZE == 32 )) || {
|
||||
echo "ERROR: PP_SIZE * TP_SIZE must equal 32" >&2
|
||||
return 2
|
||||
}
|
||||
(( TP_SIZE % EP_SIZE == 0 )) || {
|
||||
echo "ERROR: TP_SIZE must be divisible by EP_SIZE" >&2
|
||||
return 2
|
||||
}
|
||||
prepare_nodes
|
||||
write_manifest
|
||||
stop_service
|
||||
collect_gpu before
|
||||
log "starting current TP32/EP4/FlashInfer service under Nsight"
|
||||
log "starting PP${PP_SIZE}/TP${TP_SIZE}/EP${EP_SIZE}/FlashInfer service under Nsight"
|
||||
start_node 1
|
||||
start_node 2
|
||||
start_node 3
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user