13 KiB
Calculet NPU 性能压测、故障注入与验收规范
日期:2026-08-02
适用:当前 batch=1 基线、异步执行器、后续 batch calbin/新模型
1. 为什么必须重建基线
历史 CSV 有 163 行,可保留作趋势参考,但不能作为正式结论:脚本把 model 固定写为 deepseek,TTFT 又被算成 predicted_ms + prompt_ms,不是客户端收到首 token 的真实时间。
新基线必须从客户端单调时钟采集首字节/首 token,同时由服务和 Runtime 用 request/job id 对齐队列、H2D、infer、D2H、后处理。任何无法对齐的时间单独标 unattributed,不得塞进 TTFT。
2. 时钟和事件定义
2.1 客户端事件
t0 request_start 客户端准备发请求前
t1 request_headers_sent HTTP headers/body 已写完
t2 response_headers 收到响应 headers
t3 first_event 收到第一个有效 SSE/JSON event
t4 first_token 收到第一个非空生成 token
t5 last_token 收到最后生成 token
t6 response_end 流/连接正常结束
所有本进程间隔用 CLOCK_MONOTONIC/steady clock。UTC wall clock 只用于跨进程关联,需记录 NTP offset,不用于精确 duration。
2.2 指标公式
TTFH = t2 - t0 # time to response headers
TTFE = t3 - t0 # time to first stream event
TTFT = t4 - t0 # 真实 time to first generated token
E2E = t6 - t0
decode_duration = t5 - t4 # output_tokens > 1
TPOT = decode_duration/(output_tokens-1)
output_tokens_per_s = (output_tokens-1)/decode_duration
若接口在第一个 event 同时返回多个 token,要记录 chunk token count,不能用 event 数当 token 数。output_tokens<=1 时 TPOT 为 null。
2.3 服务事件
accepted -> slot_assigned -> admitted -> job_prepared -> submit_start
-> submit_ok -> wait_done -> logits_ready -> sampled -> token_flushed
Prefill/decode 分开,每 token step 有 step_index。Runtime 时长:input_transfer_ms、wait_ms、output_transfer_ms;服务还要测 input prepare、BF16 conversion、sampling。
3. 原始请求 schema
每个请求一行 JSONL,不直接把复杂数组塞 CSV:
{
"schema":"calculet-perf-request/v1",
"run_id":"...",
"request_id":"...",
"model_alias":"qwen3",
"model_generation":42,
"calbin_sha256":"...",
"software":{"server_commit":"...","image_digest":"...","calrt":"0.7.6","driver":"...","firmware":"..."},
"hardware":{"board_serial_hash":"...","chips":2,"power_mode":"..."},
"workload":{"prompt_tokens":128,"requested_output_tokens":256,"actual_output_tokens":256,"concurrency":4,"stream":true,"seed":1234},
"client_ns":{"request_start":0,"headers_sent":0,"response_headers":0,"first_event":0,"first_token":0,"last_token":0,"response_end":0},
"durations_ms":{"ttfh":0,"ttfe":0,"ttft":0,"tpot":0,"e2e":0},
"server_ms":{"queue":0,"prefill":0,"decode_total":0,"postprocess":0,"sampling":0},
"runtime_ms":{"h2d_prefill":0,"infer_prefill":0,"d2h_prefill":0,"h2d_decode":0,"infer_decode":0,"d2h_decode":0},
"result":{"http_status":200,"finish_reason":"stop","token_hash":"...","error_code":null},
"quality":{"clock_complete":true,"server_trace_joined":true,"warmup":false,"throttled":false}
}
原始 prompt 内容默认不落性能日志;记录 corpus sample id 和 token hash,避免隐私泄露。
4. step 和设备 schema
高粒度数据单独写 steps.jsonl:
{"request_id":"...","job_id":123,"seq_id":7,"kind":"decode","step":12,
"queue_ms":0.2,"prepare_ms":0.1,"h2d_ms":0.1,"device_wait_ms":15.4,
"d2h_ms":0.3,"bf16_ms":0.2,"sampling_ms":0.1,"engine":"auto",
"kv_before":139,"kv_after":140,"output_status":"done"}
设备采样 device.jsonl:wall timestamp、monotonic offset、per-chip temperature/power/frequency/memory、pending/finished/left jobs、reset counter。每项带单位;取不到的字段为 null,不用 0 代替。
5. run manifest
schema: calculet-perf-run/v1
run_id: 20260802-qwen3-baseline-001
purpose: baseline
model:
alias: qwen3
generation: 42
calbin_sha256: REQUIRED
software:
server_commit: REQUIRED
image_digest: REQUIRED
calrt: 0.7.6
driver: REQUIRED
firmware: REQUIRED
hardware:
board: REQUIRED
chips: 2
power_mode: REQUIRED
environment:
ambient_c: REQUIRED
vpn_in_path: false
client:
host: REQUIRED
transport: http-sse
matrix: configs/baseline.yaml
warmup_requests: 10
measured_requests: 100
timeout_s: 900
NPU 性能客户端应与服务处在稳定低抖动 LAN;VPN 只用于管理,不应位于性能数据路径。若无法避免,记录 RTT/jitter 并把 Runtime 设备指标与客户端指标分开解释。
6. 基线矩阵
6.1 单请求
| 维度 | 值 |
|---|---|
| prompt tokens | 1, 16, 17, 128, 512, 1024, 2048, 4096, 8192, 16384, 32768, 40959 |
| output tokens | 1, 32, 256 |
| concurrency | 1 |
| sampling | greedy(数值基线),固定 seed 的业务 sampling |
| stream | true/false |
prompt=40959, output>1 是边界拒绝/截断策略测试,不是成功生成 256 token 的性能 case。
6.2 并发
| concurrency | arrival | 目的 |
|---|---|---|
| 1 | closed-loop | 单请求下界 |
| 2,4,8,16 | closed-loop | 饱和吞吐/排队 |
| 目标 QPS 0.25/0.5/1/2... | open-loop Poisson | 真实排队和 overload |
| burst 2/4/8/16 | 同时到达 | admission/backpressure |
closed-loop 每个 worker 收到结果后再发请求,会隐藏 overload;容量和 P99 必须用 open-loop 复测。
6.3 混合流量
- 70% 短 prompt(128)/短 output(32),20% 中(2K/128),10% 长(16K/256)。
- decode-heavy:已有 KV 的长生成。
- prefill-heavy:长 prompt/短输出。
- 长 prefill 与短 decode 同时到达,观察 head-of-line blocking。
7. batch calbin 矩阵
对 B4/B8/B16 每个产物分别测 active lanes 1..B,不满 lane 的 mask/填充策略必须由厂商说明。
fill_ratio = active_lanes / compiled_batch
effective_tokens_s = successful_generated_tokens / wall_time
lane_waste = 1 - useful_lane_steps / total_lane_steps
同时报告单请求 TPOT P99 和 aggregate tokens/s。若 B16 aggregate 更高但低负载 TPOT/P99 不满足 SLA,可采用多 profile 或调度阈值,不能笼统宣称“B16 最优”。
8. 测量执行顺序
- 校验 release manifest、hash 和设备版本。
- 空闲采样 5 分钟,确认温度/功耗/频率稳定。
- 跑固定 golden,失败立即终止性能测试。
- 每个新模型/shape 10 次 warmup,不计入测量。
- case 顺序随机化或 baseline/candidate 交错,降低温漂偏差。
- 每 case 至少 30 次;短 case 100 次。
- 每轮记录 idle gap、温度和 throttle;触发 throttle 的结果分层报告。
- 测后再跑 golden,并检查资源回落。
9. 统计和比较
每个 cell 输出 count、success/error、P50/P90/P95/P99/max、mean/std、MAD、95% bootstrap CI。吞吐使用整个稳态窗口而非逐请求 tokens/s 的简单平均。
baseline/candidate 同一 case 计算配对差值:
delta_abs = candidate - baseline
delta_pct = (candidate/baseline - 1)*100
延迟负值改善,吞吐正值改善。报告应明确方向,避免表格误读。
异常值不随意删除。只有客户端 clock 缺失、进程启动 warmup、明确网络断链等预注册条件可排除,并报告排除数量/理由。
10. 正式指标面板
10.1 请求
- request rate、success、4xx/5xx/timeout/cancel。
- TTFT、TPOT、E2E 按 model/prompt/output/concurrency 分桶。
- generated tokens/s、prompt tokens/s、active sequences。
- queue time、admission wait、slot wait。
10.2 Runtime
- jobs in-flight、pending/finished/left。
- H2D/infer/D2H、OutputBuf status。
- buffer pool total/free/leased/quarantine/wait。
- KV allocated/free/wait/reject、sequence length histogram。
- CCU exception、timeout、busy、reset、generation change。
10.3 系统/设备
- process RSS/CPU/thread count/open FDs。
- device DRAM/SRAM usage(单位经确认后)。
- per-chip temp/power/frequency/throttle。
- D2D/DDR/PCIe bytes/cycles(厂商 trace)。
11. 故障注入架构
不要通过真的破坏样机文件或任意写寄存器做日常注入。优先在我方 wrapper 注入返回码/延迟/状态;设备级 reset 只在维护窗口和厂商契约下测试。
struct CalrtFaultInjector {
virtual CalrtError_e before_submit(const JobContext &) = 0;
virtual std::optional<CalrtError_e> override_wait(const JobContext &) = 0;
virtual std::optional<OutputStatus> override_status(const JobContext &) = 0;
virtual void corrupt_output(JobContext &) = 0;
};
生产编译默认注入器 disabled;启用需要 test build 或受控 admin flag,普通请求不能设置。
12. 故障场景清单
12.1 请求/资源
| 场景 | 注入 | 预期 |
|---|---|---|
| 非法长度/溢出 | 0、max+1、uint 边界 | 提交前 400,不影响设备 |
| KV 满 | canAllocate=false |
有界等待后 429/503,CPU 不忙等 |
| buffer pool 满 | 占满 lease | 排队/timeout,无共享复用 |
| cancel waiting | 等待队列中取消 | 立即移除并释放 reservation |
| cancel submitted | infer 后取消 | 仍 drain Wait,不交付结果 |
12.2 Runtime
| 场景 | 注入 | 预期 |
|---|---|---|
| submit DeviceBusy | wrapper rc=17 | 有界 backoff/失败,不 reset |
| submit DeviceUnavailable | rc=18 | 停 admission,进入恢复 |
| Wait Timeout | rc=13 | buffer/KV quarantine |
| CCU exception | output status | 不交付 logits,generation 恢复 |
| invalid shape/dtype | rc=21/22 | 隔离 artifact,不反复 reset |
| stale completion | 延迟旧 job 到 reset 后 | generation check 阻断写入 |
| output NaN/Inf | corrupt hook | 数值检查失败、golden 复测 |
12.3 生命周期
| 场景 | 操作 | 预期 |
|---|---|---|
| shutdown with queue | queued+submitted | queued 取消,submitted drain |
| load while ready | 新 generation | 先 drain,原子切换 |
| configure failure | 错 calbin | 旧 generation 保持或可回滚 |
| reset failure | mock soft reset fail | 实例 Failed,由 supervisor 重启 |
| repeated fault | 连续 timeout/crash | 指数退避和 reset 限频 |
12.4 硬件维护窗口
可在明确授权下测试 PCIe/driver/device reset,但不清理审计日志掩盖实验。每次记录操作者、时间、在途 job、前后版本/health、影响用户和恢复结果。
13. 稳定性阶段
| 阶段 | 时长 | 负载 | 通过条件 |
|---|---|---|---|
| smoke | 10 min | C1、短请求 | golden、零错误 |
| dev soak | 1 h | 70% 目标负载 | 无 leak/hang/abort |
| candidate | 8 h | 混合+burst | SLA、恢复、资源稳定 |
| release | 24 h | 目标稳态+周期 burst | 零未恢复 device fault |
| endurance | 72 h 可选 | 生产模拟 | 观察热/长期碎片 |
资源泄漏判定:warmup 后对 RSS、device memory、leased buffer、KV allocated、FD/thread 做 Theil-Sen slope 或至少起止/周期趋势;不能仅看最终值。每个 sequence 完成后 KV 和 lease 最终回到预期基线。
14. 验收阈值
14.1 正确性和可靠性是硬门槛
- 固定 golden 100% 通过;无请求间 logits/KV 串扰。
- 24h 无
abort()、无限等待、use-after-free、未处理 exception。 - 所有 fault scenario 状态转换符合设计,恢复后 golden 通过。
- success response 中 NaN/Inf=0;错误响应不含未初始化输出。
14.2 性能门槛
基线重构(in-flight=1):TTFT/TPOT P50 退化 <=3%,P99 <=5%。
优化候选:目标 cell 至少改善 8% 或端到端关键指标改善 5%;非目标 workload P99 退化 <=5%;功耗/温度不突破设备/产品上限。
ping/pong:aggregate tokens/s 提升 >=10%,单请求 TPOT P99 退化 <=5%,24h 稳定。
batch calbin:在目标并发下 aggregate tokens/s 相对 B1 提升必须给出明确目标;在数据到位前不预设倍数。低填充率必须仍满足交互 SLA。
14.3 容量门槛
在目标 arrival rate 下,P99 queue time、reject rate 和利用率同时满足 SLA。稳定最大 QPS 定义为 30 分钟窗口内错误率/延迟合格且队列不持续增长的最高点,不是短时峰值。
15. 自动化 pass/fail
validate manifests/hashes
-> golden before
-> warmup
-> execute randomized matrix
-> join client/server/device records
-> quality filters
-> summarize + confidence intervals
-> fault matrix
-> golden after
-> resource leak analysis
-> acceptance rules
-> signed report
程序退出码:0 pass;1 correctness/reliability fail;2 artifact/config invalid;3 infrastructure invalid(例如客户端时钟或网络断链);4 performance regression。基础设施失败不算产品 pass 或 fail,需重跑。
16. 报告必须呈现的反例
- 最慢的 20 个请求及其 queue/device/postprocess 分解。
- 发生过的所有 Runtime 非 success,即使最终请求重试成功。
- 每次 reset/reconfigure 和恢复时长。
- 被排除样本及理由。
- candidate 赢/输的 workload,不只展示最佳 case。
- 长 context TPOT 曲线;当前历史趋势约在 33K/40K 明显下降,必须用新 harness 重测。
17. 厂商数据接口要求
正式归因仍需要:per-op/per-chip cycles、D2D/DDR/PCIe bytes、队列 counters 语义、温度/功耗/频率 API、trace schema/时钟/开销。没有这些字段时仍可完成端到端 A/B,但结论只到图/服务级,不虚构 kernel 瓶颈。