[Benchmark] Add Kimi-K3 PP16 prefill results

This commit is contained in:
Zhiyi Hong 2026-08-21 17:08:26 +08:00
parent d790df39b2
commit 0433fcc3ee
2 changed files with 102 additions and 35 deletions

View File

@ -36,6 +36,7 @@ RoCE 执行;等待通信的时间已经超过实际计算时间。
| PP2 / TP16 | 16 | 一个 Stage 跨 2 个节点 | | PP2 / TP16 | 16 | 一个 Stage 跨 2 个节点 |
| PP4 / TP8 | 8 | 一个 Stage 完整落在 1 个节点 | | PP4 / TP8 | 8 | 一个 Stage 完整落在 1 个节点 |
| PP8 / TP4 | 4 | 每个节点放置 2 个 Stage | | PP8 / TP4 | 4 | 每个节点放置 2 个 Stage |
| PP16 / TP2 | 2 | 每个节点放置 4 个 Stage |
PP 加深后会新增 Pipeline Activation 的 Stage-to-Stage 传输,也会产生流水线 PP 加深后会新增 Pipeline Activation 的 Stage-to-Stage 传输,也会产生流水线
填充和排空造成的 bubble因此 PP 并不是越大越快。它可能带来收益的原因是: 填充和排空造成的 bubble因此 PP 并不是越大越快。它可能带来收益的原因是:
@ -48,7 +49,7 @@ PP 加深后会新增 Pipeline Activation 的 Stage-to-Stage 传输,也会产
因此这一阶段要回答的不是“PP 能不能启动”,而是:在 Kimi-K3、6000D 因此这一阶段要回答的不是“PP 能不能启动”,而是:在 Kimi-K3、6000D
和固定 Prefill 压力下,缩小 TP 通信域的收益能否超过 Pipeline 开销,以及 和固定 Prefill 压力下,缩小 TP 通信域的收益能否超过 Pipeline 开销,以及
拐点出现在 PP2、PP4 还是 PP8 拐点出现在 PP2、PP4、PP8 还是 PP16
## 3. 实现方式 ## 3. 实现方式
@ -63,7 +64,7 @@ run_pp_baseline_search.sh
脚本负责: 脚本负责:
1. 检查 601-604 的模型、镜像与 `/dev/infiniband` 1. 检查 601-604 的模型、镜像与 `/dev/infiniband`
2. 依次启动 PP2/TP16、PP4/TP8、PP8/TP4 服务; 2. 依次启动 PP2/TP16、PP4/TP8、PP8/TP4、PP16/TP2 服务;
3. 通过 `/get_server_info` 校验实际 PP、TP、EP 和 MoE backend 3. 通过 `/get_server_info` 校验实际 PP、TP、EP 和 MoE backend
4. 每个配置执行 C=8/16各重复 3 次; 4. 每个配置执行 C=8/16各重复 3 次;
5. 校验每轮是否完成 40/40 请求以及是否存在请求错误; 5. 校验每轮是否完成 40/40 请求以及是否存在请求错误;
@ -86,7 +87,7 @@ python3 -m sglang.launch_server \
--model-path /data/hf_models/Kimi-K3 \ --model-path /data/hf_models/Kimi-K3 \
--tp-size "${TP_SIZE}" \ --tp-size "${TP_SIZE}" \
--pp-size "${PP_SIZE}" \ --pp-size "${PP_SIZE}" \
--ep-size 4 \ --ep-size "${EP_SIZE}" \
--nnodes 4 \ --nnodes 4 \
--node-rank "${NODE_RANK}" \ --node-rank "${NODE_RANK}" \
--dist-init-addr 174.1.60.1:20000 \ --dist-init-addr 174.1.60.1:20000 \
@ -94,6 +95,9 @@ python3 -m sglang.launch_server \
--chunked-prefill-size 8192 --chunked-prefill-size 8192
``` ```
脚本默认取 `EP_SIZE=min(4, TP_SIZE)`。因此 PP2/4/8 使用 EP4PP16 因 TP2
必须使用 EP2满足 SGLang 的 `TP % EP == 0` 约束。
实验没有复制同事旧代码,也没有注入 `patch_pp_group.py`。准备阶段曾验证过 实验没有复制同事旧代码,也没有注入 `patch_pp_group.py`。准备阶段曾验证过
一版运行时 PP Group patch但它会让 Python Object 通信和 Tensor/P2P 通信 一版运行时 PP Group patch但它会让 Python Object 通信和 Tensor/P2P 通信
复用错误的 Gloo Group出现消息大小不匹配。回到当前 SGLang 上游实现后, 复用错误的 Gloo Group出现消息大小不匹配。回到当前 SGLang 上游实现后,
@ -123,7 +127,7 @@ NCCL_IB_GID_INDEX=3
| GPU | 601-60432 × RTX 6000D | | GPU | 601-60432 × RTX 6000D |
| MoE runner | FlashInfer MXFP4 | | MoE runner | FlashInfer MXFP4 |
| A2A backend | none | | A2A backend | none |
| EP | 4 | | EP | PP1/2/4/8 为 4PP16 为 2 |
| 输入/输出 | 16,384 / 1 token | | 输入/输出 | 16,384 / 1 token |
| Chunked Prefill | 8,192 token | | Chunked Prefill | 8,192 token |
| 并发 | C=8、C=16 | | 并发 | C=8、C=16 |
@ -175,6 +179,7 @@ chunk16K、EP1/EP8、PP16、旧镜像和运行时 PP patch因此旧数据不
```text ```text
kimi3-pp-upstream-20260821-120208 kimi3-pp-upstream-20260821-120208
kimi3-pp16-20260821-161721
``` ```
服务启动时间分别为: 服务启动时间分别为:
@ -184,9 +189,11 @@ kimi3-pp-upstream-20260821-120208
| PP2 / TP16 / EP4 | 417 s | | PP2 / TP16 / EP4 | 417 s |
| PP4 / TP8 / EP4 | 405 s | | PP4 / TP8 / EP4 | 405 s |
| PP8 / TP4 / EP4 | 338 s | | PP8 / TP4 / EP4 | 338 s |
| PP16 / TP2 / EP2 | 538 s |
PP2、PP4、PP8 共形成 18 个正式样本。每个样本均完成 40/40 请求,原始结果 PP2、PP4、PP8 共形成 18 个正式样本;补充的 PP16 形成 6 个正式样本。
中的 errors 为空。服务和 benchmark 日志没有发现: 每个样本均完成 40/40 请求,原始结果中的 errors 为空。服务和 benchmark
日志没有发现:
- CUDA OOM / OutOfMemory - CUDA OOM / OutOfMemory
- Traceback / Exception - Traceback / Exception
@ -197,6 +204,13 @@ PP2、PP4、PP8 共形成 18 个正式样本。每个样本均完成 40/40 请
PP8 在 C=8 和 C=16 下三次 Input TPS 的极差都小于 0.7%,说明结果不是某 PP8 在 C=8 和 C=16 下三次 Input TPS 的极差都小于 0.7%,说明结果不是某
一次运行的偶然峰值。 一次运行的偶然峰值。
PP16 首次启动在 decode CUDA Graph 捕获时触发 custom all-reduce IPC buffer
注册错误;关闭 PP16 的 decode graph 后16-stage warmup 又超过默认 300 秒
watchdog。正式 PP16 使用 `--disable-decode-cuda-graph --watchdog-timeout
3000000`538 秒后健康。该差异不改变本轮 Prefill 路径:输出仅 1 token
没有迭代 Decode但 PP16 的 Decode 性能不能由本实验推断。PP16 三次 Input
TPS 极差不超过 0.7%,结果同样稳定。
## 7. 性能结果 ## 7. 性能结果
下表均为 3 次正式运行的中位数: 下表均为 3 次正式运行的中位数:
@ -207,14 +221,20 @@ PP8 在 C=8 和 C=16 下三次 Input TPS 的极差都小于 0.7%,说明结果
| 2 / 16 / 4 | 8 | 5,015.95 | 25.22 s | 26.69 s | +53.96% | -35.65% | | 2 / 16 / 4 | 8 | 5,015.95 | 25.22 s | 26.69 s | +53.96% | -35.65% |
| 4 / 8 / 4 | 8 | 7,155.20 | 17.28 s | 18.62 s | +119.62% | -55.90% | | 4 / 8 / 4 | 8 | 7,155.20 | 17.28 s | 18.62 s | +119.62% | -55.90% |
| 8 / 4 / 4 | 8 | **8,809.50** | **13.47 s** | **16.45 s** | **+170.40%** | **-65.64%** | | 8 / 4 / 4 | 8 | **8,809.50** | **13.47 s** | **16.45 s** | **+170.40%** | **-65.64%** |
| 16 / 2 / 2 | 8 | 6,701.04 | 17.94 s | 21.39 s | +105.68% | -54.22% |
| 1 / 32 / 4 | 16 | 3,260.14 | 78.39 s | 80.88 s | 基线 | 基线 | | 1 / 32 / 4 | 16 | 3,260.14 | 78.39 s | 80.88 s | 基线 | 基线 |
| 2 / 16 / 4 | 16 | 5,018.38 | 50.70 s | 52.14 s | +53.93% | -35.32% | | 2 / 16 / 4 | 16 | 5,018.38 | 50.70 s | 52.14 s | +53.93% | -35.32% |
| 4 / 8 / 4 | 16 | 7,152.58 | 34.84 s | 35.95 s | +119.39% | -55.56% | | 4 / 8 / 4 | 16 | 7,152.58 | 34.84 s | 35.95 s | +119.39% | -55.56% |
| 8 / 4 / 4 | 16 | **8,824.12** | **27.31 s** | **29.77 s** | **+170.67%** | **-65.16%** | | 8 / 4 / 4 | 16 | **8,824.12** | **27.31 s** | **29.77 s** | **+170.67%** | **-65.16%** |
| 16 / 2 / 2 | 16 | 6,700.96 | 35.88 s | 39.49 s | +105.54% | -54.23% |
从 PP1 到 PP8Input TPS 沿着 PP2、PP4、PP8 单调上升TTFT 也同步下降。 从 PP1 到 PP8Input TPS 沿着 PP2、PP4、PP8 单调上升TTFT 也同步下降;
这说明在当前 C=8/16 压力下,流水线仍有足够工作可以填充;在测试范围内, PP16 则出现反转。相对 PP8PP16 在 C=8/16 的 Input TPS 分别下降 23.93%
缩小 TP 通信域的收益尚未被更深 PP 的 bubble 抵消。 和 24.06%TTFT p50 分别增加 33.18% 和 31.37%。这说明继续把 TP4 缩到
TP2 的收益已无法抵消 16-stage bubble、更多 P2P 边界和调度开销。
PP16 同时把 EP4 改为 EP2因此它不是严格的纯 PP 单变量对照。但它准确代表
32 卡上可实际部署的 `PP16/TP2` 组合,足以判定该组合不应替代 PP8 基线。
C=16 的 TTFT 大致是 C=8 的两倍,而同一 PP 下 Input TPS 基本不变。这表明 C=16 的 TTFT 大致是 C=8 的两倍,而同一 PP 下 Input TPS 基本不变。这表明
这批 Prefill 工作已接近该配置的稳定吞吐区:增加并发主要增加排队等待,而不是 这批 Prefill 工作已接近该配置的稳定吞吐区:增加并发主要增加排队等待,而不是
@ -231,8 +251,9 @@ C=16 的 TTFT 大致是 C=8 的两倍,而同一 PP 下 Input TPS 基本不变
| 2 / 16 / 4 | 80,335 MiB | 5,316 MiB | | 2 / 16 / 4 | 80,335 MiB | 5,316 MiB |
| 4 / 8 / 4 | 82,417 MiB | 3,234 MiB | | 4 / 8 / 4 | 82,417 MiB | 3,234 MiB |
| 8 / 4 / 4 | 79,407 MiB | 6,244 MiB | | 8 / 4 / 4 | 79,407 MiB | 6,244 MiB |
| 16 / 2 / 2 | 79,989 MiB | 5,662 MiB |
PP 的 Stage 不会获得完全相同的层、输出头和状态,因此 PP2/4/8 的显存不再 PP 的 Stage 不会获得完全相同的层、输出头和状态,因此 PP 配置的显存不再
像 PP1 那样均匀。PP4 的最重 Stage 已使用约 82.4 GiBPP8 的最重 Stage 像 PP1 那样均匀。PP4 的最重 Stage 已使用约 82.4 GiBPP8 的最重 Stage
约 79.4 GiB在本轮反而保留了最多的最坏 Rank 余量。 约 79.4 GiB在本轮反而保留了最多的最坏 Rank 余量。
@ -242,18 +263,18 @@ A2A 后启动或 Prefill OOM 的情况。
## 9. 通信结果如何解释 ## 9. 通信结果如何解释
本轮 PP 搜索没有为 PP2/4/8 再录制 Nsight也没有在每个 benchmark 前后采集 本轮 PP 搜索没有为 PP2/4/8/16 再录制 Nsight也没有在每个 benchmark 前后采集
独立 HCA Counter因此不能把 PP1 的 57.27% NCCL 占比直接写成 PP8 的实测 独立 HCA Counter因此不能把 PP1 的 57.27% NCCL 占比直接写成 PP8 的实测
通信占比。 通信占比。
能够被当前证据直接证明的是: 能够被当前证据直接证明的是:
1. PP1 的同口径 Nsight 确认 TP32 Prefill 以暴露的 AllReduce 为主; 1. PP1 的同口径 Nsight 确认 TP32 Prefill 以暴露的 AllReduce 为主;
2. PP2/4/8 的 `/get_server_info` 证明实际 PP/TP/EP 与计划一致; 2. PP2/4/8/16`/get_server_info` 证明实际 PP/TP/EP 与计划一致;
3. Rank 布局使 PP4 的 TP8、PP8 的 TP4/EP4 都限制在单节点内; 3. Rank 布局使 PP4 的 TP8、PP8 的 TP4/EP4 都限制在单节点内;
4. PP2/4/8 的 NCCL P2P communicator 成功建立并完成全部请求; 4. 所有 PP 配置的 NCCL P2P communicator 成功建立并完成全部请求;
5. 随 TP 通信域缩小,端到端 Input TPS 按 PP1 < PP2 < PP4 < PP8 单调提升 5. Input TPS 按 PP1 < PP2 < PP4 < PP8 提升随后 PP16 回落表明通信域
与“跨节点 TP AllReduce 是主要瓶颈”的 Profile 结论一致 缩小确有收益,但 Deep PP 开销在 PP8 之后成为更大的限制
因此当前结果支持通信域重构这一解释但没有伪造一个未经采样的“PP8 NCCL 因此当前结果支持通信域重构这一解释但没有伪造一个未经采样的“PP8 NCCL
占比”。如需进一步拆解 PP8 内部剩余时间,应单独执行短窗口 Nsight而不是 占比”。如需进一步拆解 PP8 内部剩余时间,应单独执行短窗口 Nsight而不是
@ -282,6 +303,9 @@ FlashInfer MXFP4
`PP4 / TP8 / EP4` 保留为回退对照:如果某个 A2A backend 对 PP8/TP4 有当前 `PP4 / TP8 / EP4` 保留为回退对照:如果某个 A2A backend 对 PP8/TP4 有当前
代码未覆盖的约束,可以快速判断问题来自 backend 兼容性,还是 A2A 本身。 代码未覆盖的约束,可以快速判断问题来自 backend 兼容性,还是 A2A 本身。
`PP16 / TP2 / EP2` 不进入下一阶段默认矩阵。它相对 PP1 仍有明显收益,但比
PP8 慢约 24%,并引入 decode graph 兼容性、超长启动 warmup 和 EP2 变量。
这项结果不能外推到低并发 C=1、长 Decode、在线混合流量或更多节点。当前 这项结果不能外推到低并发 C=1、长 Decode、在线混合流量或更多节点。当前
TTFT 即使在 PP8 下仍为 13.47 sC8和 27.31 sC16距离 5 s 目标仍有 TTFT 即使在 PP8 下仍为 13.47 sC8和 27.31 sC16距离 5 s 目标仍有
明显差距。Deep PP 解决了一个主要通信瓶颈,但不是 Prefill 优化的终点。 明显差距。Deep PP 解决了一个主要通信瓶颈,但不是 Prefill 优化的终点。
@ -325,6 +349,21 @@ PP1 原始结果:
results/kimi3-ep4-moe-full-20260818-151349/ results/kimi3-ep4-moe-full-20260818-151349/
``` ```
PP16 正式结果:
```text
/data/hzy/sskj/experiments/pro6000/
kimi3_pro6000_sglang_pp_baseline_search/
results/kimi3-pp16-20260821-161721/
```
PP16 启动兼容性证据(未产生 benchmark 数据):
```text
results/kimi3-pp16-20260821-155734/ # custom AR decode graph IPC 错误
results/kimi3-pp16-20260821-160555/ # 默认 300s watchdog 超时
```
PP1 Nsight 通信归因: PP1 Nsight 通信归因:
```text ```text

View File

@ -22,8 +22,8 @@ NODE_HOSTS=(174.1.60.1 174.1.60.2 174.1.60.3 174.1.60.4)
SSH_OPTS=(-o BatchMode=yes -o StrictHostKeyChecking=no -o ConnectTimeout=10) SSH_OPTS=(-o BatchMode=yes -o StrictHostKeyChecking=no -o ConnectTimeout=10)
DIST_PORT="${DIST_PORT:-20000}" DIST_PORT="${DIST_PORT:-20000}"
EP_SIZE=4 MAX_EP_SIZE=4
PP_SIZES=(2 4 8) read -r -a PP_SIZES <<< "${PP_SIZES_OVERRIDE:-2 4 8 16}"
CONCURRENCIES=(8 16) CONCURRENCIES=(8 16)
INPUT_LEN=16384 INPUT_LEN=16384
OUTPUT_LEN=1 OUTPUT_LEN=1
@ -83,6 +83,19 @@ tp_for_pp() {
echo $((32 / pp)) echo $((32 / pp))
} }
ep_for_tp() {
local tp="$1"
local ep="$MAX_EP_SIZE"
if (( tp < ep )); then
ep="$tp"
fi
(( tp % ep == 0 )) || {
echo "ERROR: TP=$tp must be divisible by EP=$ep" >&2
return 1
}
echo "$ep"
}
preflight() { preflight() {
require_password require_password
[[ -d "$MODEL_PATH" ]] || { echo "ERROR: missing model $MODEL_PATH" >&2; exit 2; } [[ -d "$MODEL_PATH" ]] || { echo "ERROR: missing model $MODEL_PATH" >&2; exit 2; }
@ -96,8 +109,12 @@ preflight() {
echo "image=$DOCKER_IMAGE" echo "image=$DOCKER_IMAGE"
echo "model=$MODEL_PATH" echo "model=$MODEL_PATH"
echo "pp_sizes=${PP_SIZES[*]}" echo "pp_sizes=${PP_SIZES[*]}"
echo "tp_sizes=16 8 4" local pp tp ep
echo "ep_size=$EP_SIZE" for pp in "${PP_SIZES[@]}"; do
tp="$(tp_for_pp "$pp")"
ep="$(ep_for_tp "$tp")"
echo "layout=pp${pp}_tp${tp}_ep${ep}"
done
echo "shape=${INPUT_LEN}->${OUTPUT_LEN}" echo "shape=${INPUT_LEN}->${OUTPUT_LEN}"
echo "chunked_prefill_size=$CHUNKED_PREFILL_SIZE" echo "chunked_prefill_size=$CHUNKED_PREFILL_SIZE"
echo "concurrencies=${CONCURRENCIES[*]}" echo "concurrencies=${CONCURRENCIES[*]}"
@ -137,10 +154,17 @@ collect_gpu_snapshot() {
} }
start_node() { start_node() {
local rank="$1" pp="$2" tp="$3" local rank="$1" pp="$2" tp="$3" ep="$4"
local host="${NODE_HOSTS[$rank]}" name bootstrap local host="${NODE_HOSTS[$rank]}" name bootstrap pp16_args=""
name="$(container_name "$rank")" name="$(container_name "$rank")"
bootstrap="export SGLANG_HOST_IP=174.1.60.$((rank + 1)) && exec python3 -m sglang.launch_server --model-path ${MODEL_PATH} --served-model-name ${SERVED_MODEL_NAME} --tp-size ${tp} --pp-size ${pp} --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 ${CHUNKED_PREFILL_SIZE} --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}" # PP16/TP2 currently fails while registering custom-all-reduce IPC buffers
# for decode graph capture. This prefill-only OSL=1 experiment does not use
# iterative decode. Its 16-stage startup also exceeds the default 300-second
# watchdog while lazily creating adjacent-stage P2P communicators.
if (( pp == 16 )); then
pp16_args="--disable-decode-cuda-graph --watchdog-timeout 3000000"
fi
bootstrap="export SGLANG_HOST_IP=174.1.60.$((rank + 1)) && exec python3 -m sglang.launch_server --model-path ${MODEL_PATH} --served-model-name ${SERVED_MODEL_NAME} --tp-size ${tp} --pp-size ${pp} --ep-size ${ep} --nnodes 4 --node-rank ${rank} --dist-init-addr ${HEAD_HOST}:${DIST_PORT} --trust-remote-code --moe-runner-backend flashinfer_mxfp4 --chunked-prefill-size ${CHUNKED_PREFILL_SIZE} --mem-fraction-static 0.88 --cuda-graph-max-bs-decode 16 ${pp16_args} --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 cmd=( local -a cmd=(
docker run -d --name "$name" docker run -d --name "$name"
--gpus all --network host --ipc=host --ulimit memlock=-1 --gpus all --network host --ipc=host --ulimit memlock=-1
@ -182,9 +206,9 @@ wait_health() {
} }
verify_server() { verify_server() {
local pp="$1" tp="$2" info="${RESULT_ROOT}/metadata/pp${pp}_server_info.json" local pp="$1" tp="$2" ep="$3" info="${RESULT_ROOT}/metadata/pp${pp}_server_info.json"
curl --fail --silent "http://${HEAD_HOST}:${PORT}/get_server_info" >"$info" curl --fail --silent "http://${HEAD_HOST}:${PORT}/get_server_info" >"$info"
python3 - "$info" "$pp" "$tp" "$EP_SIZE" <<'PY' python3 - "$info" "$pp" "$tp" "$ep" <<'PY'
import json, sys import json, sys
path, pp, tp, ep = sys.argv[1], *map(int, sys.argv[2:]) path, pp, tp, ep = sys.argv[1], *map(int, sys.argv[2:])
@ -200,21 +224,22 @@ PY
} }
start_service() { start_service() {
local pp="$1" tp local pp="$1" tp ep
tp="$(tp_for_pp "$pp")" tp="$(tp_for_pp "$pp")"
ep="$(ep_for_tp "$tp")"
stop_service stop_service
log "starting PP=${pp} TP=${tp} EP=${EP_SIZE} on 601-604" log "starting PP=${pp} TP=${tp} EP=${ep} on 601-604"
start_node 1 "$pp" "$tp" start_node 1 "$pp" "$tp" "$ep"
start_node 2 "$pp" "$tp" start_node 2 "$pp" "$tp" "$ep"
start_node 3 "$pp" "$tp" start_node 3 "$pp" "$tp" "$ep"
sleep 5 sleep 5
start_node 0 "$pp" "$tp" start_node 0 "$pp" "$tp" "$ep"
if ! wait_health "$pp"; then if ! wait_health "$pp"; then
collect_service_logs "pp${pp}_startup_failed" collect_service_logs "pp${pp}_startup_failed"
stop_service stop_service
return 1 return 1
fi fi
verify_server "$pp" "$tp" verify_server "$pp" "$tp" "$ep"
collect_service_logs "pp${pp}_healthy" collect_service_logs "pp${pp}_healthy"
collect_gpu_snapshot "pp${pp}_healthy" collect_gpu_snapshot "pp${pp}_healthy"
} }
@ -269,7 +294,8 @@ for path in sorted((root / "raw").glob("*.jsonl")):
continue continue
data = next(json.loads(line) for line in path.open(encoding="utf-8") if line.strip()) data = next(json.loads(line) for line in path.open(encoding="utf-8") if line.strip())
pp, concurrency, repeat = map(int, match.groups()) pp, concurrency, repeat = map(int, match.groups())
row = {"pp_size": pp, "tp_size": 32 // pp, "ep_size": 4, tp = 32 // pp
row = {"pp_size": pp, "tp_size": tp, "ep_size": min(4, tp),
"concurrency": concurrency, "repeat": repeat, "concurrency": concurrency, "repeat": repeat,
"completed": data.get("completed")} "completed": data.get("completed")}
row.update({name: data.get(name) for name in metrics}) row.update({name: data.get(name) for name in metrics})
@ -287,7 +313,8 @@ for row in rows:
groups.setdefault((row["pp_size"], row["concurrency"]), []).append(row) groups.setdefault((row["pp_size"], row["concurrency"]), []).append(row)
summary = [] summary = []
for (pp, concurrency), group in sorted(groups.items()): for (pp, concurrency), group in sorted(groups.items()):
item = {"pp_size": pp, "tp_size": 32 // pp, "ep_size": 4, tp = 32 // pp
item = {"pp_size": pp, "tp_size": tp, "ep_size": min(4, tp),
"concurrency": concurrency, "repeats": len(group)} "concurrency": concurrency, "repeats": len(group)}
for name in metrics: for name in metrics:
values = [float(row[name]) for row in group if row.get(name) is not None] values = [float(row[name]) for row in group if row.get(name) is not None]
@ -328,10 +355,11 @@ run_matrix() {
} }
dry_run() { dry_run() {
local pp tp local pp tp ep
for pp in "${PP_SIZES[@]}"; do for pp in "${PP_SIZES[@]}"; do
tp="$(tp_for_pp "$pp")" tp="$(tp_for_pp "$pp")"
echo "PP=${pp} TP=${tp} EP=${EP_SIZE} ${INPUT_LEN}->${OUTPUT_LEN} C=${CONCURRENCIES[*]} chunk=${CHUNKED_PREFILL_SIZE}" ep="$(ep_for_tp "$tp")"
echo "PP=${pp} TP=${tp} EP=${ep} ${INPUT_LEN}->${OUTPUT_LEN} C=${CONCURRENCIES[*]} chunk=${CHUNKED_PREFILL_SIZE}"
done done
bash -n "$0" bash -n "$0"
} }