图像系统性能、配额与诊断
规格 ID:TC010 — 稳定标识(页面改名/迁移不变),全站对照见设计规格索引。
实现边界:
AcquisitionSession上有ResourceDiagnosticsSnapshot(接收/拒绝/预览替换、入口高水位、查询深度、tracked_buffers)。hold_duration已接实体时钟(微秒;BufferEntity::born_at于注册时记录,实体已擦除则报 0,不伪造时长)。类型名刻意避开Diagnostic:toolgroup::Diagnostic仍只是编排码表。已有跨层配额总账QuotaLedger(imaging/quota_ledger.hpp)与稳态测量门禁(tests/smoke_quota_ledger.cpp);显示层仍无账,按「不可用」报告而非 0。
关联:overview · image-model · memory-management · camera-acquisition · session-lifecycle · output-inspection · 本地预览
目标设计与 API 草案,尚未实施。总体边界和推进状态见设计概述。
编排权威见 toolgroup.mdx。本文只负责跨层配额与诊断口径;不定义执行调度。采集入口队列成本与 ToolGroup
RunSession成本分列计量,避免把会话写成图/队列调度器。
本文负责跨图像、工具、采集入口、调试和显示的成本口径与诊断;格式扩展及显示账本各在对应专题维护。
关联:模型 · ToolGroup(编排) · 显示预算 · 验证 · 采集 · 停止
配额作用域与成本归属
| 作用域 | 计量对象 | 释放依据 |
|---|---|---|
| 存储协调实体 | 完整分配/外借成本、访问和归还状态 | 真实归还成功;失败单独隔离,不伪报空闲 |
ToolGroup RunSession | 该次/该会话在途图像句柄、临时与暂存输出、同 session 互斥下的 Run | 对应所有权退出,包含尚未结束的退休工作;不含采集入口队列调度语义 |
| 相机适配 | SDK 外借槽和正在归还的引用 | SDK 确认归还,而非工具的 Task 返回 |
| 输出存储与调试 | 最近集合、独立副本、历史、打开的查看者 | 各持有者关闭;淘汰缓存不关闭查看者 |
| ImageDisplay | 保留输入、显示副本、mip、上传暂存和纹理 | UI/后台/渲染的实际退出及原有账本规则 |
相同 BufferId 的多个视图可以共享像素成本,但仍有 handle 和队列对象成本。同一分配在多个组件的局部读数中可能重复出现,只有明确作用域的去重账本才可汇总为物理保留量。SDK 槽位、托管分配字节、GPU 纹理和每秒复制带宽分别报告,不合并成一个含糊的“内存占用”。
热路径与内存成本
同步可授予时使用 ValueTask 快速路径,不为每次读取启动 Task.Run。清理回调、权限唤醒和用户 continuation 都在协调锁外执行;任务完成采用异步 continuation 方式,避免锁内重入。取消注册在请求结束时解除,取消只让一个授予/释放结果获胜。
handle 保持引用类型,避免可复制 struct IDisposable 造成多份伪所有权。不默认复用暴露给用户的 handle 对象,防止旧引用访问到被重新分配的新资源。物理像素存储可以池化;访问状态池化若实施必须证明没有 ABA、取消回调或尚未消费的 ValueTask 指向已复用状态。公开 ValueTask 按一次消费规则使用,需重复等待的归还事实用 Task。
任务/归还信号尽可能延迟创建,未订阅诊断的普通路径不捕获调用栈。可以按工具执行多个行任务,但它们共享同一独占写入阶段,调用方负责写区域不重叠,并在 Complete 前全部结束;不扩展为多个 ROI 并发写锁。长时间 native pin 计入持有时长及 pinned bytes,不能只统计托管对象数量。
大图操作按行进行取消检查和范围检查,避免要求整个 native 长容量存储构成一个 int 长度 Span。克隆整理成受控私有排列,精确保留原始格式。清理、临时色彩转换、复制和上传的峰值一起计量;吞吐优化不能以延迟归还 SDK 到无限队列为代价。
会话账本与诊断
核心可提供可选的资源诊断快照:BufferId、Generation/有效状态、保留字节、handle 数、活动读/写、等待申请、pin、归还状态与持有时长。会话、队列和预览适配另记接收/拒绝/替换计数、队列高水位、在途工作、归还延迟与归还失败。多个层级读数有不同含义,不用“工具的 Task 已结束”推导内存已归还。
诊断注册在实际归还终态解除,不能用全局强引用表永久保存完整图像或工具。详尽工具/调用栈追踪按需开启,普通路径以计数和标识为主。Debug 检测未完成写入、重复交接、跨期访问和会话外保留;检测结果不能触发不安全的强制回收。
实施验证至少覆盖持续运行后内存和外借槽位达到稳定平台、停流后回到基线、归还延迟尾部、吞吐与丢弃率、复制带宽、UI 卡顿及故障恢复。报告实际格式、尺寸、采集速率、图像池/队列容量、SDK 协议、机器与后端;在没有测量前不承诺固定帧率、零分配或零泄漏。
场景与证据口径
| 场景 | 必须观察的结果 |
|---|---|
| 稳态取图、算法和显示 | 内存/槽位达到平台,吞吐与端到端延迟符合配置,不随累计帧数增长 |
| UI 暂停、慢分支、调试窗口长开 | 只占已定义容量,丢弃/拒绝计数与策略一致,不积累隐含回调 |
| SDK 归还变慢或失败 | 队列及在途归还有界,故障可定位,不重复 Release 或宣称已回收 |
| native 取消晚于工具取消 | pin 与原图保留到实际访问结束,没有提前复用 |
| 连续启动/停止与重新挂载 | 本会话拥有的资源正常回到基线,外部保留单独解释 |
这些是验收要求,不是本轮文档拆分已经取得的性能结论。实际平台、负载矩阵与退出条件统一见验证与完善路线。
实现现状(按实)
| 规格期望 | 当前实现 |
|---|---|
| 资源诊断快照 | ResourceDiagnosticsSnapshot 在 acquisition_session.hpp(M4-10);NON-STABLE |
| BufferId / Generation | tracked_buffers[] 带 id / generation(实体仍在册时) |
| 持有时长 | 字段在;已接实体时钟(微秒,born_at 起算;实体擦除报 0) |
| 接收/拒绝/替换、入口高水位 | posts_accepted / posts_rejected / preview_replaces / ingress_high_water |
StopSummary 深度 | outstanding / active_access / grant_queue / return_queue 仍在停流侧 |
toolgroup::Diagnostic | 编排码表;勿与本快照混名 |
| 保留字节 / pin / 跨层配额总账 / 稳态门禁 | 已有统一账 QuotaLedger(字节/引用/槽位三单位,口径见下节);稳态门禁在 smoke_quota_ledger(平台 + 回基线 + 反例自检) |
| 显示 / GPU 账 | 无账:Display 层报 available=false,不以 0 冒充已测 |
跨层配额总账与稳态门禁(TC010 落地口径)
为什么必须先定口径:跨层读数原本无法汇总,因为同一分配会在「池、实体、会话、显示」四处各出现一次,直接相加就是重复计数;而「队列深度」与「保留字节」混在一个数里则连量纲都不成立。QuotaLedger 因此把每条读数绑定到唯一单位,单位决定它能被怎样聚合:
| 单位 | 含义 | 允许的聚合 | 禁止 |
|---|---|---|---|
| Bytes | 物理保留 | 按 BufferId 去重,每个分配只在其所有者处计一次 | 把池总量与实体总量相加(重复计数);按视图/句柄数乘容量 |
| References | 逻辑所有权(句柄、队列引用、pin、读/写者) | 按所有者计数求和 | 换算成字节;与字节相加 |
| Slots | 有界队列占用 | 只与该队列自身容量比较 | 跨实例把容量相加当成一个总限额 |
由此得到三条可机器校验的不变量(QuotaLedgerSnapshot::Consistent()):
- 层的单位必须与其作用域匹配(Storage/Pool → Bytes;SessionRefs → References;Ingress/GrantQueue/ReturnWork → Slots)。
- 字节守恒:
attributed + unattributed == total。total以分配器的 outstanding bytes 为权威;attributed是按 BufferId 去重后的实体容量和;两者之差是未解释的残留,稳态下不得增长。 - 槽位:
used <= capacity(capacity 非 0 时),高水位同受此约束。
无账层不得报 0:显示/GPU 账尚未实现,Display 层报 available=false 且不带数字——「不可用」与「实测为 0」是两件事。
稳态测量门禁(QuotaSteadyState,tests/smoke_quota_ledger.cpp)定义为三条同时成立:
- 平台:按窗口比较,末窗最大值不得超过首窗最大值(容差默认 0),且末窗内部离散度不超过容差——单调增长即失败。
- 回基线:全部句柄释放、停流泵完之后,字节回到基线、
tracked_buffers == 0、有界队列槽位归零。 - 有判别力:门禁本身要用反例自检(喂入单调增长的序列必须被拒;单位错配、字节不守恒、槽位超容量的快照必须被
Consistent()拒绝)。只会在健康数据上通过的检查不算门禁。
门禁当前覆盖「连续启动/停止 24 轮(3 窗口)+ 入口满容 + 视图去重」,并按规格要求报告(不武断断言)max_hold_duration_us 作为归还延迟尾部。真实吞吐、复制带宽、UI 卡顿与 GPU 预算仍需实机与真实算法,仍属未验收项。
文档状态:本站规格。