Skip to content

fix(perception): share tracking ONNX sessions across cameras - #477

Open
housq wants to merge 1 commit into
XiaoMi:mainfrom
housq:agent/share-tracking-onnx-sessions
Open

fix(perception): share tracking ONNX sessions across cameras#477
housq wants to merge 1 commit into
XiaoMi:mainfrom
housq:agent/share-tracking-onnx-sessions

Conversation

@housq

@housq housq commented Jul 31, 2026

Copy link
Copy Markdown

What changed

  • add an engine-scoped TrackingModelResources pool that lazily creates one
    detector session and one ReID session
  • inject those sessions into each camera's detector/ReID wrapper
  • keep every camera's DeepSORT/SORT tracker, Kalman state, track IDs, and
    embedding history independent
  • release camera-local wrappers before dropping the shared session references
  • reuse the same ReID session for the registration fallback path

Why

Each camera previously constructed its own detector and ReID
onnxruntime.InferenceSession. With multiple camera channels this duplicated
model weights, native thread pools, and allocator workspaces even though the
sessions are safe to use concurrently.

For three active camera channels, this changes the tracking model session count
from six to two without sharing mutable tracking state.

Impact

In a three-camera deployment, warm RSS decreased from roughly 5.36 GiB to about
3.0 GiB (around 40–45%). CPU inference frequency is unchanged by this PR.

The resource pool is optional at the tracking-service API boundary, so existing
standalone construction and tests keep the previous behavior.

Validation

  • cd backend && MILOCO_HOME=<isolated-dir> uv run pytest -q
    • 2798 passed
  • targeted model-resource and fallback tests
    • 6 passed
  • Ruff on the affected identity modules and tests
  • ty on the new production resource module

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


PPG seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@github-actions

Copy link
Copy Markdown

👋 感谢提交 PR @housq!维护者会尽快 review。

提交前请确认:

  • CI 全绿(test / lint / build)
  • 改动聚焦单一主题,便于审阅
  • 若改动了依赖(lockfile / pyproject.toml / package.json),需维护者评论 /allow-dependencies-change <当前 head SHA> 放行(之后再 push 需重新放行)

@housq
housq force-pushed the agent/share-tracking-onnx-sessions branch from 73290e5 to 970f0a3 Compare July 31, 2026 06:32
@housq
housq marked this pull request as ready for review July 31, 2026 06:54
@github-actions

Copy link
Copy Markdown

[PR #477]: fix(perception): share tracking ONNX sessions across cameras

作者: housq (Siqing Hou)
范围: agent/share-tracking-onnx-sessions → main

修改方案

要解决的问题:每个摄像头通道独立创建检测(det_4C.onnx)和 ReID(human_body_reid_v2.onnx)的 ONNX InferenceSession,导致模型权重、原生线程池和分配器工作区在多通道间重复。三通道部署下跟踪模型 session 数为 6 个,RSS 偏高。

整体方案:引入 engine 级别的 session 池,在同一个 PerceptionEngine 的所有摄像头之间共享不可变的 ONNX 推理 session,同时保持每个摄像头的 tracker 状态(Kalman 滤波、track ID、embedding 历史)完全独立。

  1. 新增 TrackingModelResources session 池

    • (resolved_path, use_gpu, num_threads) 为缓存 key,对同一模型路径懒加载创建且仅创建一个 InferenceSession(TrackingModelResources.get_session
    • session 创建由 threading.Lock 保护,防止摄像头 pipeline 并发启动时重复创建;推理本身不加锁,保持完全并发
    • 路径统一 Path.resolve() 到绝对路径,避免符号链接或相对路径导致缓存 miss
  2. 通过构造函数注入共享 session

    • DetectorHumanReID 新增可选 session 参数:传入时跳过 make_session 直接复用;未传入时保持原有自建行为,向后兼容(Detector.initHumanReID.init
    • PerceptionEngine.__init__ 在非 mock 模式下创建唯一的 TrackingModelResources 实例,通过 _tracking_service_kwargs 传给每个 RealTrackingService / DeepSortTrackingServiceapi.py
    • DeepSortTrackingService 额外把 ReID session 透传给 DeepSortTrackerHumanReID,形成 detector 和 ReID 两路 session 均被全摄像头共享
  3. 注册兜底路径复用 session

    • get_reid_extractor 的 fallback HumanReID 也通过 TrackingModelResources.get_session 拿 session,与 tracker 侧走同一个缓存(api.py
    • 附带修正:fallback 的 use_gpu 从硬编码 False 改为读配置 perception_use_gpu,与 tracker 侧行为对齐
  4. 确定性关闭顺序

    • close() 先释放所有 per-camera wrapper(tracker / detector / fallback 的 release() → 将各自 session 属性置 None),再最后释放 TrackingModelResourcesapi.py
    • 保证共享 InferenceSession 在所有消费方 wrapper 引用断开后才从缓存中移除,GC 按 refcount 自然回收原生资源
关闭顺序(自上而下释放):

IdentityEngine.close()     ← per-camera worker 清理
    ↓
TrackingService.release()  ← 各摄像头 tracker/detector wrapper 断引用
    ↓
fallback.release()         ← 兜底 HumanReID wrapper 断引用
    ↓
TrackingModelResources     ← 最后清除 session 缓存 dict
    .release()

关键设计原则

  1. 共享的是不可变推理资源,不共享可变跟踪状态:ONNX InferenceSession 的 run() 线程安全,多个摄像头可并发调用同一个 session;但 Kalman 滤波器、track ID 计数器、embedding 历史等必须按摄像头隔离。PR 严格区分了这两层
  2. 向后兼容session 参数在所有层级(Detector / HumanReID / DeepSortTracker / TrackingService)均为可选,不传则保持原有自建 session 行为。独立测试和 CLI 工具无需改动
  3. mock 模式零开销:mock 模式下 _tracking_model_resources 为 None,所有 if model_resources is not None 检查短路,不创建任何 session

测试覆盖

主线 测试文件 用例摘要
session 池并发安全 test_model_resources.py::test_session_created_once_under_concurrent_first_access 8 线程 × 32 次并发请求同一模型,断言只创建 1 个 session;release 后 count 归零
共享 session + 独立 tracker test_model_resources.py::test_two_cameras_share_sessions_but_not_trackers 两个 DeepSortTrackingService 共享 detector/ReID session,tracker 和 _mot 实例独立
关闭顺序 test_model_resources.py::test_engine_close_releases_wrappers_before_shared_sessions 断言释放顺序为 identity → tracking → fallback → resources
fallback 路径 test_get_reid_extractor_fallback.py::test_fallback_reuses_engine_session 有 model_resources 时 fallback 走共享 session
fallback 向后兼容 test_get_reid_extractor_fallback.py::test_fallback_uses_resolved_abs_path_and_caches 无 model_resources 时 fallback 保持自建 session、绝对路径

问题

🔵 建议(可选优化)

  • model_resources.py:27num_threads 参数已接受但未在生产路径接入
    • 背景: TrackingModelResources.__init__ 接受 num_threads: int | None = None,并把它纳入缓存 key (resolved_path, use_gpu, num_threads)。但 PerceptionEngine 创建 TrackingModelResources 时未传此参数,始终走 None
    • 问题: 当前不影响正确性(make_session 内部对 None 默认取 _DEFAULT_NUM_THREADS = 4,与旧行为一致),但 API 表面暗示支持 per-engine 线程数配置而实际无法触达。若未来需要按部署场景调整线程数,需要额外改动 PerceptionEngine.__init__ 和配置 schema 才能接入
    • 改进: 如果短期不打算暴露该配置,可以在 TrackingModelResources 构造函数中去掉 num_threads 参数(以及缓存 key 中的对应分量),保持 API 与实际能力一致;或从 IdentityEngineConfig 读取并传入:
      self._tracking_model_resources = TrackingModelResources(
          use_gpu=self._config.identity.perception_use_gpu,
          num_threads=self._config.identity.perception_num_threads,  # 新增配置字段
      )

结论

LGTM — 设计清晰、实现完整。session 共享机制正确利用了 ORT InferenceSession 的并发安全性,关闭顺序保证 wrapper 引用先于共享缓存释放,向后兼容性好。测试覆盖了并发创建、多摄像头共享/隔离、关闭顺序等关键路径。三通道部署 RSS 从 ~5.36 GiB 降至 ~3.0 GiB(~40-45%)是显著优化。num_threads 未接入是唯一的 API 一致性小瑕疵,不影响功能。


由 review-pr skill v1.6 生成

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants