# 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 客户端事件 ```text 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 指标公式 ```text 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 服务事件 ```text 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: ```json { "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`: ```json {"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 ```yaml 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/填充策略必须由厂商说明。 ```text 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. 测量执行顺序 1. 校验 release manifest、hash 和设备版本。 2. 空闲采样 5 分钟,确认温度/功耗/频率稳定。 3. 跑固定 golden,失败立即终止性能测试。 4. 每个新模型/shape 10 次 warmup,不计入测量。 5. case 顺序随机化或 baseline/candidate 交错,降低温漂偏差。 6. 每 case 至少 30 次;短 case 100 次。 7. 每轮记录 idle gap、温度和 throttle;触发 throttle 的结果分层报告。 8. 测后再跑 golden,并检查资源回落。 ## 9. 统计和比较 每个 cell 输出 count、success/error、P50/P90/P95/P99/max、mean/std、MAD、95% bootstrap CI。吞吐使用整个稳态窗口而非逐请求 tokens/s 的简单平均。 baseline/candidate 同一 case 计算配对差值: ```text 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 只在维护窗口和厂商契约下测试。 ```cpp struct CalrtFaultInjector { virtual CalrtError_e before_submit(const JobContext &) = 0; virtual std::optional override_wait(const JobContext &) = 0; virtual std::optional 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 ```text 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 瓶颈。