diff --git a/README.md b/README.md
index 6fc2a21..ea0f4a1 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,9 @@
# sskj — 多平台大模型推理性能基准测试项目
+> **更新(2026-07-30 18:40:53 CST)**
+>
+> 为 DeepSeek-V4-Pro 双机 TP16 quick-map 的唯一入口新增 `CASE_IDS` 场景过滤和未知 Case 预检,可在完整九点实验前先跑 1K/32K Prefill 与 C1/C32 Decode 四点 Sanity;运行清单会记录实际过滤条件。同步精简阶段档案:正文只保留最终成功 Run 与有效结论,历史失败压缩到末尾经验教训。
+>
> **更新(2026-07-30 17:54:30 CST)**
>
> 为 DeepSeek-V4-Pro 双机 TP16 quick-map 加入 RDMA fail-closed 启动保护。唯一 Shell 入口现在只允许计算网 `eth0/eth3` 与其 RDMA HCA `mlx5_0/mlx5_3`,在两端预检并透传 `rdma_cm/uverbs0/uverbs3`,服务健康后必须从两端 NCCL INFO 日志证明 `NET/IB` 和两条 HCA 均已启用,否则 benchmark 不会开始。运行清单新增 RDMA 开关、强制校验和设备路径;语法、结果解析器单测、完整 dry-run 及非法网卡/HCA 负例均已通过,真机 NET/IB 验证与 Phase 1 重跑尚未执行。
diff --git a/docs/dsv4pro_pro6000d_2node_sglang/phase1_dsv4pro_pro6000d_2node_sglang_quick_map.html b/docs/dsv4pro_pro6000d_2node_sglang/phase1_dsv4pro_pro6000d_2node_sglang_quick_map.html
index 7c6b70f..1d3f41c 100644
--- a/docs/dsv4pro_pro6000d_2node_sglang/phase1_dsv4pro_pro6000d_2node_sglang_quick_map.html
+++ b/docs/dsv4pro_pro6000d_2node_sglang/phase1_dsv4pro_pro6000d_2node_sglang_quick_map.html
@@ -237,21 +237,9 @@
返回推理优化主计划
- 阶段状态:网络错误已定位,RDMA fail-closed 代码已完成,等待真机验证和重跑。
- 第一次真机 Run
- dsv4pro-pro6000d-2node-sglang-quick-20260730-140026
- 已验证双机服务可用,但因发现请求量和 Prefix Cache 口径问题而主动停止。
- 精简后的第二次 Run
- dsv4pro-pro6000d-2node-sglang-quick-v2-20260730-143625
- 完成 3 个 Prefill 固定点后曾观察到约 65 token/s。
- 后续控制组确认 quick-map 容器看不到 RDMA,NCCL 回退
- NET/Socket,同时脚本把 Socket 接口设成了低速非计算网。
- 因此这 3 个结果不能作为模型、算子或 TP16 的性能基线,Phase 2 暂停,
- 当前入口已固定计算网 eth0/eth3 与
- mlx5_0/mlx5_3,完成设备透传、日志强校验和 dry-run;
- 只有真机日志证明 NET/IB 后才会重跑 Phase 1。
- Manifest 终态为 ABORTED_EARLY_FOR_PHASE2;
- 两节点容器和 16 张 GPU 已清理。
+ 阶段状态:等待真实双 Rail RDMA 重跑。
+ 启动器与静态验证已完成;历史无效 Run 不进入最终分析。下一次仅在
+ NCCL 日志通过 NET/IB 门禁后执行 Sanity 和完整 quick map。
1. 目标与边界
@@ -265,7 +253,7 @@
只测试 SGLang,不测试 vLLM。
使用双机 16 卡完整实例,不做 PD 分离。
不启用 MTP、EAGLE、DSpark 或其他投机解码。
- 本轮不启用 Profiler;受错误 Socket 网络影响的三个旧 Case 仅保留为事故证据,不再作为端到端基线。
+ 本轮不启用 Profiler;正文只记录最终有效 Run,失败尝试仅在末尾总结经验。
不修改或调用旧的全天全量 Benchmark 脚本。
@@ -542,211 +530,41 @@ wait "${background_pid}"
| 完整 Dry-run | 通过 | 服务、九个固定点、混合 A/B、清理均展开成功 |
| 真实旧 Bench JSON 解析 | 通过 | 成功解析 P50/P95/P99 与吞吐字段 |
| 项目精简 | 通过 | 实验目录顶层仅保留一个 Shell 入口 |
- | 双机容器启动 | 通过 | 第二次 Run 于 14:42:04 通过 Health Check,启动约 5 分 30 秒 |
- | Prefix Cache 隔离 | 通过 | Warm-up 后 POST /flush_cache 返回 200,正式请求仍为 #cached-token: 0 |
- | 中止清理 | 通过 | 头、Worker 节点均无相关容器和 Bench 进程,16 张 GPU 显存回到 0 MiB |
+ | RDMA fail-closed | 静态验证通过 | 非法网卡、缺失设备或未出现 NET/IB 时禁止 benchmark |
- 9. 真机结果
-
- 最终采用 Run
- dsv4pro-pro6000d-2node-sglang-quick-v2-20260730-143625。
- 以下三条均为一条请求、C=1、OSL=1,且正式测量前
- Prefix Cache 已清空。
+
9. 最终真机结果
+
+ 正确双 Rail RDMA 下的正式结果尚未生成。本节只接受通过传输门禁、
+ 冷缓存口径和结果校验的最终 Run;完成后将写入四点 Sanity、九个固定点、
+ 混合 A/B 与阶段结论。
-
- | Case |
- ISL |
- 输入 TPS |
- TTFT |
- E2E |
- 状态 |
-
+ | 门禁或实验 | 状态 | 最终证据 |
- short_prefill_latency_1k_c1 | 1K | 64.44 tok/s | 15.88 s | 15.88 s | COMPLETED |
- mid_prefill_latency_32k_c1 | 32K | 64.96 tok/s | 504.44 s | 504.44 s | COMPLETED |
- long_prefill_latency_128k_c1 | 128K | 65.20 tok/s | 2010.38 s | 2010.38 s | COMPLETED |
- mid_prefill_throughput_32k_c16 | 32K × 16 | 未形成最终结果 | 未形成最终结果 | 未形成最终结果 | ABORTED |
+ 双 Rail NET/IB | 待真机验证 | 头、Worker 日志均需识别 mlx5_0/mlx5_3 |
+ | 四点 Sanity | 待执行 | 1K/32K Prefill 与 C1/C32 Decode |
+ | 九个固定点 | 待执行 | 最终 summary.csv 与 report.md |
+ | 混合 A/B | 待执行 | Control 与 Treatment 的 Decode 指标变化 |
-
- 这些数值已被降级为“错误网络路径复现”。
- 它们可以证明低速 Socket 路径近似随输入长度线性增长,但不能用于判断
- DeepSeek-V4-Pro、SGLang Kernel、GPU 算力或正确 TP16 数据面的性能。
-
-
- 9.1 对错误网络 Run 可以下的结论
+ 10. 经验教训
- - 三个长度的输入吞吐只相差约 1.2%,稳定在
64.44–65.20 token/s。
- - 32K TTFT 约为 1K 的 31.76 倍;128K TTFT 约为 32K 的 3.99 倍,几乎按 token 数线性增长。
- - 这不是偶发卡顿:服务端每约 125 秒完成一个 8192-token Chunk,GPU 在该期间持续忙碌。
- 32K, C=16 的观察窗口内,日志持续显示 #new-seq: 1,队列从 15 降到 14;至少当前路径没有立即把 16 条 Prefill 合成大批次。
- - 没有出现 OOM、CUDA、EngineDead 或请求失败,但 NCCL 没有使用预期 RDMA 数据面。
+ - 启动参数不等于实际传输路径;开始性能测试前必须由 NCCL 日志证明
NET/IB。
+ - Warm-up、固定随机种子和跨 Case Prefix Cache 会改变 TTFT,冷缓存与热缓存必须分开报告。
+ - 传输门禁或测量口径不成立时,性能结果不能用于归因模型、调度器或 Kernel。
+ - 完整实验前先跑少量 Sanity 点,能更早发现环境和口径问题。
-
- 9.2 现在还不能下的结论
-
- - 仅凭 GPU Utilization=100% 不能判断是算力、显存带宽还是通信瓶颈。
- - 不能用本轮断言 SGLang Bug、DSV4/NSA Kernel、MoE Backend 或 Scheduler 存在性能问题。
- - Decode、Balanced 和混合 A/B 未执行,Phase 1 不提供这些场景的基线。
- - 每个 Shape 只有一次重复,不能用于稳定性或 CV 结论。
-
-
- 9.3 为什么提前结束
- 当时根据约 65 token/s 的稳定信号提前停止第 4 个 Case,并写入
- EARLY_STOP_FOR_PHASE2。后续网络控制组表明这个停止动作仍然避免了
- 无效算力消耗,但调查方向需要修正:不是立即进入 Kernel/硬件归因,而是先校正
- NCCL 数据面并重跑快速地图。
-
-
- 9.4 为什么旧脚本的 TTFT 短很多:缓存与网络两个因素
-
- 2026-07-30 对旧目录
- /data/qqt/sskj/experiments/pro6000/dsv4_pro6000_sglang_tp16
- 做了逐项审计,并追加原网络配置冷请求控制组。最终结论是:
- 旧结果与 quick-map 既不是同一缓存口径,也不是同一 Socket 网络路径。
-
-
-
- | 审计项 | 旧脚本 | quick-map | 判断 |
-
-
-
- | 服务端配置 |
- 同一镜像,TP16 / EP2,8K Chunk,FlashInfer MXFP4 MoE |
- 模型参数相同,但 NCCL/IP 接口不同 |
- 不能排除网络启动参数回归 |
-
-
- | 正式请求数 |
- C=1 仍强制至少 10 条 |
- 延迟点只发 1 条 |
- 旧均值混合了多条请求的缓存状态 |
-
-
- | Warm-up |
- 每个 Shape 固定 16 条同 Prompt Warm-up |
- Warm-up 后清 Prefix Cache;32K/128K 延迟点不做额外 Warm-up |
- 旧正式测量会继承 Warm-up 的 Prompt 前缀 |
-
-
- | Cache 清理 |
- 从不传 --flush-cache |
- 正式测量前传 --flush-cache |
- 旧脚本跨 Case、跨长度保留 Radix/Prefix Cache |
-
-
- | Shape 顺序 |
- 固定 Seed=42,按 1K→4K→8K→16K→32K→64K→128K 递增 |
- 每个延迟点按冷缓存解释 |
- 旧请求会复用上一档相同 Prompt 的短前缀 |
-
-
-
-
- 旧时间线还有一条直接证据:17:40 的失败 Run 已完整执行过
- 1K / 128 / C=1,18:01 的正式 Run 没有重启服务便再次执行同一批
- Seed=42 请求。因此旧文件中的 1K TTFT 约 0.455 秒,本身就是热缓存结果。
-
-
-
- | 最小复现 | Mean TTFT | P95 TTFT | 解释 |
-
-
- | quick-map 错误网络,1K→1 冷缓存 | 15.88–16.04 s | 15.88–16.04 s | 低速非计算网 Socket 路径 |
- | 旧原始网络,1K→1 冷缓存 | 1.458 s | 1.458 s | eth0 400G 物理端口上的 Socket 路径 |
- | 旧原始网络,32K→1 冷缓存 | 38.062 s | 38.062 s | 比 quick-map 的 504.44 s 快约 13.25 倍 |
- | quick-map,1K→128 冷缓存 | 15.79 s | 15.79 s | 排除 OSL=1 特殊慢路径,但仍受错误网络影响 |
- | 2026-07-28 旧产物 | 0.455 s | 0.513 s | 服务已被前一次 Run 和后续递增长度预热 |
-
-
-
- 旧 32K 文件的第一条请求约 0.67 秒,其余 9 条平均约 35.51 秒;
- 旧 128K 文件的第一条约 0.96 秒,其余 9 条平均约 144.83 秒。
- 后 9 条也已经分别继承上一档 16K、64K 前缀。旧报告仍按完整 ISL 统计
- Input TPS,因此会把只计算新增后缀的耗时除进完整 token 数,进一步放大吞吐。
-
-
- 缓存审计原始产物保存在:
-
- /data/hzy/dsv4_script_audit_20260730/
-
- 本地归档:
+ 历史排查细节保存在
TTFT 脚本口径审计报告
- 及同目录原始 JSON/log。
-
-
- 9.5 NCCL 数据面控制组
-
- 原脚本控制组使用其默认 NCCL_SOCKET_IFNAME=eth0,不注入
- NCCL_IB_HCA 与 NCCL_CROSS_NIC。NCCL INFO 和容器设备
- 检查给出了直接证据:
-
- NET/IB : No device found
-NET/Socket : Using [0]eth0:10.101.0.11
-Using network Socket
-
-docker exec ... ls /dev/infiniband
-# No such file or directory
-
- 宿主机本身存在 uverbs0/uverbs3/rdma_cm,且
- mlx5_0→eth0、mlx5_3→eth3 均 Up。这说明问题位于
- 容器设备透传和脚本网卡选择,不是物理计算网缺失。quick-map 与原脚本使用相同
- Docker 设备边界,却把 Socket 接口设成了低速非计算网,造成 10–13 倍退化。
- 在 NET/Socket 路径上,NCCL_CROSS_NIC=1 不参与选择。
-
-
- 修正后的唯一入口已加入三层门禁:两端字符设备预检、Docker 精确
- --device 透传、健康后 NCCL INFO 传输后端验证。静态检查、
- 结果解析器单测和完整 dry-run 已通过;非法 eth1 或
- mlx5_1 会在加载模型前被拒绝。该结论仍属于代码验证,
- 真机 NET/IB 成功证据要等下一次启动后补入。
-
-
- 网络控制组原始产物:
-
- /data/hzy/dsv4_oldscript_cold_control_20260730/
-
- 本地归档:
- TP16 网络路径审计报告
- 及同目录 NCCL/benchmark 原始日志。
-
-
-
-
-
- | 检查点 |
- 状态 |
- 结果或结论 |
-
-
-
- | 服务健康 | 通过 | 端口可用且无 OOM/Engine 异常,但 NCCL 数据面未按预期进入 RDMA |
- | 第一次固定点 Run | 主动停止 | 发现 32K 单请求约需十余分钟;原协议的 4 次同形状请求会使整轮再次接近半天 |
- | RDMA fail-closed 启动器 | 静态验证通过 | 仅允许两条计算网 Rail;设备透传、日志强校验与运行清单已加入 |
- | 精简后九个固定点 | 旧 Run 作废,待重跑 | 先完成真机 NET/IB 验证,再生成新的性能基线 |
- | 混合干扰 A/B | 未执行 | 待 Prefill 根因明确后再决定是否重放 |
- | 阶段耗时 | 约 65 分钟 | 14:36:26 启动,15:41:52 完成进程与容器清理 |
- | 是否进入下一阶段 | 否 | Phase 2 暂停;先证明双 Rail NET/IB 并重跑 Phase 1 |
-
-
-
- 10. 结果位置
- 服务器原始结果:
- /data/hzy/sskj/experiments/pro6000/
-dsv4pro_pro6000d_2node_sglang_tp16_quick_map/results/
-dsv4pro-pro6000d-2node-sglang-quick-v2-20260730-143625/
-
- 本地已归档
- report.md、
- aggregate.csv
- 和同目录下的 Manifest、Summary、主日志。
+ 与
+ TP16 网络路径审计报告,
+ 不作为本阶段最终结果。
11. 运行命令
diff --git a/docs/dsv4pro_pro6000d_2node_sglang/推理优化计划.html b/docs/dsv4pro_pro6000d_2node_sglang/推理优化计划.html
index af9af15..f661f8f 100644
--- a/docs/dsv4pro_pro6000d_2node_sglang/推理优化计划.html
+++ b/docs/dsv4pro_pro6000d_2node_sglang/推理优化计划.html
@@ -415,7 +415,7 @@
6000D 双机 DeepSeek-V4-Pro 推理优化计划
-适用环境:174.1.51.5 + 174.1.51.7,每台 8 张 RTX PRO 6000 Blackwell Server Edition
当前部署:DeepSeek-V4-Pro,16 张 GPU 组成一个完整实例
当前约束:模型暂时只能使用全部 16 张 GPU,无法额外复制一套模型进行 PD 分离
计划版本:2026-07-30 17:54 CST
+适用环境:174.1.51.5 + 174.1.51.7,每台 8 张 RTX PRO 6000 Blackwell Server Edition
当前部署:DeepSeek-V4-Pro,16 张 GPU 组成一个完整实例
当前约束:模型暂时只能使用全部 16 张 GPU,无法额外复制一套模型进行 PD 分离
计划版本:2026-07-30 18:40 CST
当前执行状态与阶段档案
+
+阶段档案规范:正文只保留最终成功实验、有效结果和结论;
+失败尝试压缩到末尾的经验教训。阶段完成时删除“暂不能下结论”等过渡内容。
+
0. 先看懂双机通信
在分析 TP16 性能前,先区分设备、传输后端与 NCCL 参数。
@@ -471,14 +475,6 @@
400 Gbit/s ≈ 50 GB/s 只是单向理论上限,NCCL 的
algbw/busbw 与端到端模型吞吐都不能直接等同于该数字。
-
-
-2026-07-30 已确认:当时容器内没有 /dev/infiniband,NCCL 日志显示
-NET/IB : No device found 并回退 NET/Socket。quick-map 又误选
-低速非计算网,因此 1K/32K 冷 Prefill 比原脚本的 eth0 Socket 路径慢约
-10.9×/13.25×。这不是 NCCL_CROSS_NIC=1 导致的。
-
-
1. 目标与原则
1.1 最终目标
在不做 PD 分离的前提下,定位 DeepSeek-V4-Pro 在双机 6000D 上的端到端瓶颈,并提高:
diff --git a/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/README.md b/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/README.md
index 2417aac..191d74a 100644
--- a/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/README.md
+++ b/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/README.md
@@ -68,6 +68,17 @@ DRY_RUN=1 bash run_quick_map.sh all
The launcher refuses to start while either node has an active GPU compute
process. `ALLOW_BUSY_GPU=1` exists only for deliberate operator override.
+Before the complete run, use the same entry for a four-point Sanity pass:
+
+```bash
+CASE_IDS="short_prefill_latency_1k_c1,mid_prefill_latency_32k_c1,decode_latency_1k_to_1k_c1,decode_throughput_1k_to_1k_c32" \
+ DRY_RUN=1 bash run_quick_map.sh fixed
+```
+
+Remove `DRY_RUN=1` only after the TP16 service has passed the `NET/IB` startup
+gate. An unknown `CASE_IDS` value fails before any benchmark request is sent.
+Leave `CASE_IDS` empty to run all nine fixed points.
+
## GPU run
Run the complete quick map in `tmux` after both nodes are free:
diff --git a/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/config.env b/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/config.env
index a7a568d..8370ab0 100644
--- a/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/config.env
+++ b/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/config.env
@@ -70,5 +70,9 @@ SCENARIO_FILE="${SCENARIO_FILE:-${SCRIPT_DIR}/quick_map_scenarios.tsv}"
RESULT_BASE="${RESULT_BASE:-${SCRIPT_DIR}/results}"
RUNTIME_BASE="${RUNTIME_BASE:-${SCRIPT_DIR}/runtime}"
+# Optional comma- or space-separated subset of quick_map_scenarios.tsv.
+# Leave empty for the complete nine-point fixed suite.
+CASE_IDS="${CASE_IDS:-}"
+
DRY_RUN="${DRY_RUN:-0}"
RESUME="${RESUME:-1}"
diff --git a/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/quick_map_results.py b/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/quick_map_results.py
index 27bb7d8..6e1878c 100755
--- a/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/quick_map_results.py
+++ b/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/quick_map_results.py
@@ -369,6 +369,7 @@ def write_manifest(args: argparse.Namespace) -> None:
"git_commit": args.git_commit,
"git_dirty": bool(args.git_dirty),
"scenario_file": args.scenario_file,
+ "case_ids": args.case_ids,
"notes": [
"The fixed quick map does not stop on SLO.",
"Profiler is disabled; these results are eligible for performance comparison.",
@@ -649,6 +650,7 @@ def add_manifest_arguments(parser: argparse.ArgumentParser) -> None:
parser.add_argument("--git-commit", required=True)
parser.add_argument("--git-dirty", type=int, required=True)
parser.add_argument("--scenario-file", required=True)
+ parser.add_argument("--case-ids", default="")
def main() -> None:
diff --git a/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/run_quick_map.sh b/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/run_quick_map.sh
index 5da247a..12c1284 100755
--- a/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/run_quick_map.sh
+++ b/experiments/pro6000/dsv4pro_pro6000d_2node_sglang_tp16_quick_map/run_quick_map.sh
@@ -55,6 +55,33 @@ run_on_node() {
fi
}
+case_selected() {
+ local candidate="$1"
+ local normalized="${CASE_IDS//,/ }"
+ local selected
+ [[ -z "${normalized//[[:space:]]/}" ]] && return 0
+ for selected in ${normalized}; do
+ [[ "${selected}" == "${candidate}" ]] && return 0
+ done
+ return 1
+}
+
+validate_case_filter() {
+ local normalized="${CASE_IDS//,/ }"
+ local selected
+ [[ -z "${normalized//[[:space:]]/}" ]] && return 0
+
+ for selected in ${normalized}; do
+ if ! awk -F $'\t' -v case_id="${selected}" \
+ '$1 == case_id { found = 1 } END { exit !found }' \
+ "${SCENARIO_FILE}"; then
+ log "ERROR: CASE_IDS contains an unknown case: ${selected}"
+ return 1
+ fi
+ done
+ log "Selected fixed cases: ${normalized}"
+}
+
validate_network_config() {
case "${ENABLE_RDMA}" in
0|1) ;;
@@ -644,7 +671,8 @@ write_run_manifest() {
--rdma-device-paths "${RDMA_DEVICE_PATHS}" \
--git-commit "${git_commit}" \
--git-dirty "${git_dirty}" \
- --scenario-file "${SCENARIO_FILE}"
+ --scenario-file "${SCENARIO_FILE}" \
+ --case-ids "${CASE_IDS}"
}
summarize_results() {
@@ -667,12 +695,14 @@ enable_result_logging() {
run_fixed_suite() {
log "Validating scenario file: ${SCENARIO_FILE}"
python3 "${RESULT_TOOL}" validate-scenarios "${SCENARIO_FILE}"
+ validate_case_filter
local completed=0 total=0
local rep case_id stage isl osl concurrency multiplier minimum warmup note
for (( rep=1; rep<=NUM_REPETITIONS; rep++ )); do
while IFS=$'\t' read -r case_id stage isl osl concurrency multiplier minimum warmup note; do
[[ -z "${case_id}" || "${case_id}" == \#* ]] && continue
+ case_selected "${case_id}" || continue
[[ -n "${PROMPT_MULTIPLIER_OVERRIDE}" ]] && multiplier="${PROMPT_MULTIPLIER_OVERRIDE}"
[[ -n "${MIN_NUM_PROMPTS_OVERRIDE}" ]] && minimum="${MIN_NUM_PROMPTS_OVERRIDE}"
@@ -698,6 +728,10 @@ run_fixed_suite() {
[[ "${DRY_RUN}" == "1" ]] || sleep "${CASE_COOLDOWN_S}"
done < "${SCENARIO_FILE}"
done
+ if (( total == 0 )); then
+ log "ERROR: fixed suite selected zero cases"
+ return 1
+ fi
log "Fixed quick map complete: completed=${completed}/${total} failed=${FIXED_FAILURES}"
}
@@ -848,6 +882,10 @@ run_standalone_suite() {
run_all() {
SERVER_ARTIFACT_DIR="${RESULT_DIR}/server"
+ log "Validating scenario file before service startup: ${SCENARIO_FILE}"
+ python3 "${RESULT_TOOL}" validate-scenarios "${SCENARIO_FILE}"
+ validate_case_filter
+
if [[ "${DRY_RUN}" != "1" ]]; then
preflight_bench_client
enable_result_logging