跳转至

D3D-HOI 预处理性能审计

结论

Canonical D3D-HOI default preprocess 已支持逐模块计时、自动阶段与多 GPU scale-up。生产环境采用一张 GPU 一个 case 的动态队列;不要在同一张 32 GB V100S 上叠加重模型。

主要代码优化集中在 SAM2:full-object 和 active-part 共用一次模型加载与一次视频 inference state,四次传播之间重置 state;只在内存保留各帧最终采用方向的二值 mask,最后每张 PNG 只编码一次,并用 16 个 CPU worker 并行完成独立文件编码。计算顺序、阈值、方向选择和最终文件语义保持不变。

Canonical 依赖关系

frames
├── segmentation: SAM2 full/active, SAM3 human/ground
├── DA3 raw depth + camera
└── CoTracker 2D input

segmentation + DA3 camera ──> SAM3D body
raw depth + body + human mask ──> metric depth alignment
CoTracker 2D + aligned depth/camera ──> 3D tracks
masks + tracks + aligned depth + body ──> object init
PartNet metadata ──> D3D active-joint metadata
RGB + full mask + body/camera ──> VLM contact raw ──> manual review

VLM raw inference 不依赖 object init,可在 GPU case 队列结束后统一运行。人工 contact review 是显式人工 gate,不计入自动预处理耗时。当前 D3D-HOI 默认的 object contact region 是跳过/静态资产路径,同样不构成耗时瓶颈。

代表性实测

测试环境为 8× NVIDIA V100S 32 GB、72 CPU;数据和输出位于本机 /DATA。以下为全新 preprocess、contact 延后时的 wall time:

case 帧数 分辨率 自动预处理 wall time 峰值显存
b001-0010_cad12561 120 1280×720 7:11.46 21,347 MiB
b004-0032_cad7221 150 1920×1080 9:32.81 27,287 MiB
b005-0001_cad10900 270 1280×720 17:47.41 25,691 MiB

其中 b004 与 b005 同时占用两张 GPU 时,makespan 为 17:47;串行时间之和约 27:20,因此对这组不均衡任务是 1.54×。更多 case 由最长任务优先的动态队列降低尾部空闲。

逐模块示例:

模块 b001-0010 b004-0032 b005-0001
segmentation 总计 139.358 s 186.660 s 320.196 s
DA3 depth 109.468 s 140.321 s 237.813 s
SAM3D body/motion 110.148 s 131.387 s 213.441 s
depth alignment 3.448 s 5.500 s 5.094 s
CoTracker tracks 47.217 s 83.592 s 266.419 s
object init 11.115 s 12.169 s 12.038 s
active joints 1.046 s 1.177 s 1.286 s
VLM contact raw(最终并发配置) 10.4 s 11.1 s 12.31 s

每次运行都会在 <case>/recon/preprocess/timing.json 保存原子更新的 stage 计时、状态和失败信息。preprocess-auto 会把 contact 记录为 skipped,reason 为 deferred_to_infer_contact_labels_batch

SAM2 等价性与收益

  • b001-0010_cad12561:最终 threaded 版本的 240 张 full/active PNG 与原基线逐字节一致;SAM2 从原双进程约 111.14 s 降至 86.37 s,约快 22.3%。
  • b004-0032_cad7221:300 张 PNG 与原基线逐字节一致;去除临时 PNG 往返后,SAM2 从 122.069 s 降至 117.194 s,进一步快 4.0%。
  • b005-0001_cad10900:shared-state combined 版本的 540 张 full/active PNG 与原基线逐字节一致;最终 in-memory writer 另在 b001/b004 上逐帧验证。
  • SAM2 优化版峰值显存约 5.2–7.1 GB,未用输出差异换取速度。

三例合计 1,080 张 SAM2 PNG 全部逐字节一致。b001 的 CoTracker tracks_2d、2D visibility 和 point source frame 也逐数组完全一致;其 3D tracks 只继承上游 fresh DA3 重跑的浮点差异。未修改代码的 DA3 两次 fresh run 平均相对差约 1.88e-5,这是 GPU 模型重跑的非确定性边界,不是 SAM2 优化造成的计算改写。

已验证但未采用的候选

  • SAM3 ground 改为单帧输入:输出等价,但没有可重复收益。
  • DA3 inference_mode/移除 empty_cache:更慢且数值输出发生变化。
  • SAM3D body batch size 40:更慢、峰值约 30.3 GB,且输出发生变化。
  • CoTracker fnet replay cache:输出等价,但从约 48.2 s 变为 91.2 s,峰值约 27.8 GB。
  • CoTracker 预构造 reverse video:输出等价,但相邻 A/B 为 54.02 s 对 52.93 s。
  • CoTracker 移除逐 query empty_cache:输出等价,但没有可重复收益。

这些候选均已从生产代码回退。当前实现只保留经过输出核对且有可重复收益的改动。

Contact 并发与时间边界

Canonical contact 已核对为 DashScope OpenAI-compatible endpoint + qwen-vl-max;凭据从 OPENAI_VLM_API_KEYDASHSCOPE_API_KEY 读取。旧 default 中的 vveai token 已失效, 且 reasoning_effort: high 与 Qwen 不兼容,因此生产配置将其显式置空。

interval 请求彼此独立。并发 A/B 结果如下:

  • b001(15 intervals):1 worker 为 84.1 s,4 workers 为 20.6 s,8 workers 为 12.1–14.2 s,最终全 interval 并发为 10.4 s。
  • b004(19 intervals):4 workers 为 25.0 s,最终全 interval 并发为 11.1 s。
  • b005(34 intervals):4 workers 为 43.1 s,16 workers 为 28.45 s,32 workers 为 13.37 s,最终全 interval 并发为 12.31 s。

最终 max_workers: 64 是上限,不会无条件创建 64 个请求。当前 256-case inventory 最多只有 38 个 interval;batch 脚本逐 case 执行,所以不同 case 不会叠加并发。34 并发实测无 429、无重试,34 个唯一响应文件齐全,排序后覆盖 0–270 全部帧。

异步完成顺序不会改变标签对应关系:每个请求/响应文件名包含唯一 interval/frame, future 完成后按 interval_idx 排序,再按显式 [start_frame, end_frame_exclusive) 写回。 专门的回归测试强制请求按 2→1→0 完成并验证最终仍按 0→1→2 写入。4/8 worker 实测的 15 张输入 crop 与 bbox JSON 也逐字节一致。

Qwen 服务在 temperature: 0 下仍有非确定性:相同 8-worker 配置重复两次,输入哈希 完全相同但 raw 文本不同,其中一次在单个 interval 的左右手判断发生漂移,另一次又 与 4-worker 标签完全一致。因此 VLM 不采用“输出逐字节相同”作为并发正确性判断, 而以请求集合、输入、interval 映射和完整性为等价边界;HTML review 仍是最终质量门。

自动 GPU wall time 不包含人工 review,也不包含静态/跳过的 object contact region。 preprocess-auto 延后 VLM,--run-contact 会在 GPU worker 结束后调用正式 batch 接口; API 失败或 raw 缺失会使整批返回非零,不会误报成功。