【开源共测】Benchmark 均值掩盖了 p99 尾部延迟?征集 MindSpore Lite / Ascend 运行时证据层共测伙伴

大家好,我最近在做一个开源项目 YERP( yolo-edge-runtime-profiler,地址:yolo-edge-runtime-profiler.git)。

做这个不是想替代 MindSpore Lite benchmark、日志系统、profiling 工具或底层性能分析器。而是想探索一层:Runtime Evidence Layer:运行时证据层

简单说,就是当端侧 AI 推理出现慢帧、尾部延迟、输出异常膨胀、后处理压力、内存压力或难以复现的现场问题时,把当时的:

  • 输入样本
  • 推理阶段耗时
  • 后处理耗时
  • 输出元数据
  • 硬件 / 系统状态
  • 触发原因

“快照”下来,并绑一起成一条本地 EvidenceFlow 证据记录,供后续定位、分析和复核。

一句话:

benchmark 告诉我们模型平均跑多快;日志告诉我们哪里报错;YERP 记录:

到底是哪一个输入样本,让端侧推理进入了值得保存、追溯、分析的压力状态?

一、为什么想做“运行时证据层”?

在做端侧 AI 部署时,很多开发者可能都遇到过类似问题:

模型在本地 benchmark 里平均耗时很好,指标看起来很漂亮。但部署到真实设备、真实业务流、真实客户现场后,会出现偶发性卡顿、p95 / p99 延迟升高,或者某些输入样本明显更慢。

最麻烦的是,这类问题往往很难复现。常见情况是:

  • 没有完整现场日志
  • 没有保存触发问题的输入样本
  • 没有当时的输出分布
  • 没有完整硬件状态
  • 只有一句反馈:现场偶尔卡一下

最后开发者只能自己在推理代码里到处打 timestamp,手动猜测:

  • 是预处理慢了?
  • 是 NPU / CPU 推理慢了?
  • 是后处理慢了?
  • 是数据拷贝慢了?
  • 是输出候选数量突然变多?
  • 是某些输入触发了特殊耗时路径?

现有工具链通常能解决两类问题:

  • Benchmark / Profiler: 告诉你模型整体性能、平均耗时、算子级耗时分布。
  • Error Logs: 告诉你哪里报错、哪里转换失败、哪里运行异常。

但在真实端侧部署中,还缺一层更贴近现场分析的问题:

到底是哪一个输入样本,把推理流水线压进了异常压力状态?

YERP 就是想做这一层,它不是底层 profiler,而是一个轻量的 运行时证据层 (Runtime Evidence Layer

当推理流水线出现异常压力时,YERP 会尝试把:

  • 输入样本
  • 各阶段耗时
  • 输出张量 / 检测结果元数据
  • 硬件或系统状态
  • 触发原因

组织成一个本地 EvidenceFlow 证据包,用来后续分析、调试、profiling、重训、量化校准或工具链排查。

二、YERP 目前已经跑通的部分

目前 YERP Vision 已经在 YOLO 场景下跑通了一个最小闭环。它可以在推理过程中监控:

preprocess_ms
inference_ms
postprocess_ms
total_ms
p50 / p95 / p99 / max latency
tail latency coefficient
postprocess spike
stage imbalance
box count
confidence entropy
class entropy
dominant cause

当某一帧触发运行时压力时,YERP 会在本地保存:

image.jpg
metadata.json
runtime_report.json
runtime_frames.csv

例如在一个已经跑通的 YERP Vision 样例中,一帧普通 YOLO 检测画面被自动捕获。它不是因为人工提前标注为“问题帧”才被保存,而是因为 YERP 检测到这一帧触发了运行时压力:


(注:该图仅用于展示 YERP Vision 已跑通的 EvidenceFlow 捕获机制,不代表 MindSpore Lite / Ascend 后端测试结果,MindSpore Lite / Ascend adapter 仍处于共测设计阶段。)

下图是这个 JSON sidecar 对应的被捕获帧。重点不是这张图“看起来异常”,而是它在运行时触发了可记录的压力信号。

{
  "state": "RED",
  "dominant_cause": "POSTPROCESS_DOMINANT",
  "selection_reason": "runtime_pressure",
  "metrics": {
    "box_count": 18,
    "class_count": 4,
    "confidence_entropy": 2.51,
    "preprocess_ms": 1.20,
    "inference_ms": 3.95,
    "postprocess_ms": 2.02,
    "postprocess_ratio": "28.15%"
  }
}

这类记录的价值在于:

不需要提前知道这张图是不是“缺陷”或“坏样本”。
它先记录:这一个输入样本是否让端侧推理管线进入了值得复盘的压力状态。

这就是我说的 EvidenceFlow:
把一次运行时压力事件,转化成可保存、可复盘、可分享字段结构的证据记录。

我也已经在 Ultralytics GitHub Discussions 里介绍过这个思路,Ultralytics 正在持续收集团队和社区反馈:
https://github.com/orgs/ultralytics/discussions/25250#discussioncomment-17677787

三、为什么想和 MindSpore Lite / Ascend 社区共测?

我希望探索的是:能否为 MindSpore Lite / Ascend / 端侧 NPU 推理场景做一个 YERP EvidenceFlow Adapter。不是替代现有工具,而是和现有工具互补。形成:

MindSpore Lite benchmark:
关注模型整体性能、平均耗时、基础推理能力。

日志 / 报错信息:
关注模型转换失败、算子报错、运行异常。

底层 profiler:
关注算子、runtime、硬件调度、内存等底层瓶颈。

YERP:
关注哪一个输入样本触发了运行时压力,
并把输入样本、耗时、输出元数据、系统状态绑定成证据记录。

我觉得这层对于端侧推理、NPU 部署、机器人、无人机、AMR、工业视觉、信创边缘设备都可能有价值。因为这些场景里的问题往往不是简单的“能不能跑”,而是:

  • 现场能不能稳定跑
  • 出了问题能不能复现
  • 能不能知道是哪一个输入样本触发了异常
  • 能不能把这类样本沉淀下来,用于后续优化、重训、量化校准或工具链排查

四、我想共建的最小 Adapter

第一版不需要很复杂。只要 MindSpore Lite / Ascend 推理脚本能提供以下信息,就可以先生成 EvidenceFlow:

sample_id / input_path
preprocess_ms
inference_ms
postprocess_ms
output_metadata
可选硬件指标

例如伪代码大概是这样:

import time

from yerp import EvidenceRecorder

recorder = EvidenceRecorder(
    backend="mindspore_lite",
    output_dir="./yerp_evidence"
)

for sample in dataset:
    t0 = time.perf_counter()
    input_tensor = preprocess(sample)
    t1 = time.perf_counter()

    # 你的 MindSpore Lite / Ascend 推理
    outputs = model.predict(input_tensor)
    t2 = time.perf_counter()

    result_meta = postprocess(outputs)
    t3 = time.perf_counter()

    recorder.update(
        sample_id=sample.id,
        input_ref=sample.path,

        preprocess_ms=(t1 - t0) * 1000,
        inference_ms=(t2 - t1) * 1000,
        postprocess_ms=(t3 - t2) * 1000,

        output_metadata=result_meta,  # 例如 shape、candidate count、top score、检测框数量等

        hardware_metadata={
            "cpu_usage": optional_cpu_usage,
            "memory_mb": optional_memory,
            "npu_usage": optional_npu_usage,
            "temperature": optional_temperature,
        }
    )

第一版甚至可以不强依赖硬件指标。只要能拿到:

  • 输入样本引用
  • 推理阶段耗时
  • 后处理耗时
  • 输出元数据

先做到:

滑动窗口基线计算
p50 / p95 / p99 延迟统计
尾部延迟检测
慢样本捕获
输出压力检测
本地 JSON / CSV / report 生成

后续再逐步接入 CPU、NPU、内存、温度、队列长度、多路 stream id 等指标。

五、希望寻找什么样的共测伙伴?

如果你正在使用 MindSpore Lite / Ascend / 端侧 NPU 推理,并且遇到过下面这些问题,欢迎一起测试:

模型转换后能跑,但某些样本推理特别慢;

benchmark 平均耗时正常,但实际业务流里偶发卡顿;

量化后模型体积变小,但端侧速度没有明显提升,甚至某些算子或某些样本耗时异常;

检测类模型在高密度目标场景下后处理突然变慢;

某些输入样本会明显拉高 p95 / p99 / max latency;

现场问题难复现,客户只反馈“偶尔卡一下”;

无法全量保存现场数据,但希望抓住最关键的异常样本。

不需要你上传商业模型、客户图片或敏感数据。

YERP 默认是 local-first

图片留在本地
模型留在本地
业务数据不需要上传
EvidenceFlow 证据记录也可以只保存在本地

如果涉及隐私场景,也可以只分享:

脱敏后的 report
字段结构
耗时分布
报错日志
bucket 统计
你遇到的现象描述

我会根据这些反馈继续调整 YERP adapter 的字段设计和脚本逻辑。


六、共测主要想验证什么?

主要想先验证几个问题:

  1. MindSpore Lite / Ascend 推理脚本中,哪些运行时字段最容易采集?
  2. 对端侧推理用户来说,最需要看的 EvidenceFlow 字段是什么?是 inference_ms、postprocess_ms、p99、内存、NPU 利用率,还是输出 tensor 统计?
  3. 对检测、分类、OCR、语音、时序模型,不同任务的 output_metadata 应该如何抽象?
  4. YERP 生成的本地 JSON / CSV / report,是否能帮助开发者更快定位“哪一个输入样本触发了异常”?
  5. 是否有必要进一步做 MindSpore Lite / Ascend 专用 adapter?

七、当前边界说明

为了避免误解,再说明一下当前边界:

YERP 目前已经在 Vision / YOLO 场景跑通了运行时压力捕获。
MindSpore Lite / Ascend adapter 还在共测和设计阶段。
YERP 不替代 MindSpore Lite 官方 benchmark、日志或 profiler。
YERP 也不直接判断底层根因,而是先保存“哪一个输入样本 + 什么运行时证据”值得继续分析。

我更希望它成为一个辅助层:

先用 YERP 找到值得分析的输入样本;
再结合 MindSpore Lite 日志、benchmark、profiling 工具继续定位底层原因。


八、一句话总结

平均推理耗时只能告诉我们模型大体跑得快不快。但真实部署里,最关键的问题往往是:

到底是哪一个输入样本,让端侧推理进入了压力状态?

YERP 想做的,就是把这个瞬间保存成 EvidenceFlow。如果你对 MindSpore Lite / Ascend / 端侧推理 EvidenceFlow adapter 感兴趣,或者手里有真实部署中的慢样本、卡顿样本、量化后异常样本,欢迎留言或私信交流。

项目目前是开源方向,欢迎共测、反馈和一起定义 adapter 接口。

如果你对端侧推理 EvidenceFlow 感兴趣,或者手里有真实的现场压力样本,欢迎留言,或者来 GitHub 仓库 提 Issue 交流(觉得好的别忘了点星 :slight_smile: )!

让我们一起把端侧排障从“玄学猜测”变成“有证可查”。

1 个赞

很好的一个想法,的确可以弥补当前基于MindSpore Lite在实际部署场景发生问题时,定位难的痛点;我们可以一起共建维测能力,提升MindSpore Lite的可维可测能力,方便使用,以及问题定位分析;

但是这个地方有一个小的疑问,线上推理是很关注性能的,实际业务部署,可能比较难接受每次推理完成,然后去做recorder.update的数据更新,这个可能是会影响业务的实际部署性能,以及内存,线上业务可能是期望,启动之后,长期不再出现问题,会一直推理下去,所以实时更新数据,是否会导致性能、内存等指标发生劣化

1 个赞

@YeFeng24 谢谢认可!
也感谢你的提醒,“性能损耗与内存劣化”这个问题非常关键,因为“探针”的第一原则就是:“探针本身不能成为新的性能瓶颈”。
实际上,YERP 在设计上并不是在每次推理后都同步保存图片、写 JSON 或做重计算。帖子中的 recorder.update() 只是伪代码,真实生产版更准确的说是两条路径:

Fast Path:推理主线程内的轻量 observe
Slow Path:后台异步 evidence 写入

1、Fast Path:只采轻量数字,不阻塞推理主线程
生产环境里,尤其是 MindSpore Lite / Ascend / IPC / AMR / 车机这类 C++ 端侧部署场景,YERP 不在主推理线程里做重操作。Fast Path 只做几件轻量事情:

读取已有 timestamp
读取已有输出元数据
更新固定长度滑动窗口
判断是否触发压力事件

不会重新推理,不复制大 tensor,不每帧存图,不每帧写 JSON。后续 C++ / 嵌入式 adapter 可以按这种方式设计:

atomic counters
fixed-size rolling window
lock-free / low-lock ring buffer
bounded memory
no dynamic allocation on hot path as much as possible

也就是说,主推理线程里只做非常轻量的 observe,不做真正的 evidence 落盘。

2.Slow Path:触发后异步写入,不在推理线程里 open/write 文件
如果某一帧真的触发了 p99 尾延迟、postprocess spike、输出压力异常等条件,YERP 也不应该在推理线程里直接:

open("xxx.json", "w")
save_image(...)

而是:

主线程只 push 一个小型 evidence event 到 bounded queue
        ↓
后台 worker thread 异步写 JSON / 图片 / report
        ↓
如果队列满了,宁可丢弃 evidence,也不阻塞推理

所以 YERP 的线上模式不是“全量录像式监控”,而是:事件触发式、限流、可采样、可关闭、可异步的运行时证据捕获。

  1. 内存有上限控制

YERP 不会无限增长保存历史,而是会提供:

window_size
max_items
cooldown_sec
metadata_only
sampling_rate
event-only mode

例如,现在已有的 hard-example metadata 里就包含 cooldown_secmax_itemsmetadata_onlytrigger_statestrigger_causes 等策略字段,这类字段就是为了避免无脑全量保存。YERP 定位也是 lightweight / zero-intrusion runtime profiler,核心是读取已有 runtime metadata,而不是改模型或重跑推理。

但是,仍要再次感谢你的提醒,因为我觉得这个问题可以显式量化,把 “YERP 自身开销”作为 adapter 的第一项指标。后续可以让 MindSpore Lite / Ascend adapter 输出:观测P50、P95、P99耗时(微秒),内存峰值,元数据开销,事件开销,包含图像保存开销,丢弃证据数量等等,验证 YERP 自身有没有引入不可接受的性能或内存劣化。
另外, 后续 YERP 的 MindSpore Lite Adapter 也可以提供几种运行模式选项:

  • dev / debug 模式:记录更多字段并保存图片,用于前期调试和共测。
  • shadow 模式:只做轻量内存观察和滑动窗口统计,只输出统计不落盘。
  • event-only 模式:静默运行,只在触发异常压力时保存 evidence。
  • off 模式:完全关闭,探针休眠,零开销。

总结就是:YERP 不是重型在线监控,而是低开销的 runtime evidence layer。主推理线程只做轻量 observe;真正的 evidence 写入放到异步 slow path;队列满了就丢 evidence,不能阻塞业务推理。

您觉得这样的办法怎么样?或者您有更好的想法我们也可以一起讨论!另外也欢迎更多的开发者提出意见和建议来讨论。