From 65a429ab336a0b86189192d622c0f97b173c39fc Mon Sep 17 00:00:00 2001 From: patrykorwat Date: Sun, 23 Aug 2026 17:30:02 +0000 Subject: [PATCH] Revert "fix(object_detection): report per-frame time, not round-trip, for async detectors" This reverts commit 92feefc113ca9fcb8c6d9d8e2a3c25730ee50208. --- frigate/object_detection/base.py | 40 ++++++++++---------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/frigate/object_detection/base.py b/frigate/object_detection/base.py index 764a5f44e7..eca9b72d73 100644 --- a/frigate/object_detection/base.py +++ b/frigate/object_detection/base.py @@ -4,6 +4,7 @@ import queue import threading import time from abc import ABC, abstractmethod +from collections import deque from multiprocessing import Queue, Value from multiprocessing.synchronize import Event as MpEvent from typing import Any @@ -185,20 +186,6 @@ class DetectorRunner(FrigateProcess): class AsyncDetectorRunner(FrigateProcess): - # Stream-based detectors (memryx, axelera) keep several frames in flight - # through a pipelined SDK, so the send_input -> receive_output round-trip of - # one frame is gated by the pipeline depth and the input cadence (measured - # latency ~= depth x input gap): at a realistic ~35 fps aggregate that reads - # ~150 ms even though the accelerator handles a frame in ~14 ms back-to-back. - # Using that as inference_speed makes a fast detector look ~10x slower than - # sync detectors, whose metric is the duration of a single detect_raw call. - # For async runners, inference_speed is therefore derived from detector - # throughput: the average time between completed results equals 1000 / - # detection_fps, which is the per-frame processing time and is comparable - # across detectors. Idle gaps (> ASYNC_RESULT_IDLE_INTERVAL between results) - # are not folded in, so a quiet stream cannot inflate the reported time. - ASYNC_RESULT_IDLE_INTERVAL = 0.5 - def __init__( self, name: str, @@ -221,7 +208,7 @@ class AsyncDetectorRunner(FrigateProcess): self._frame_manager: SharedMemoryFrameManager | None = None self._publisher: ObjectDetectorPublisher | None = None self._detector: AsyncLocalObjectDetector | None = None - self._last_result_time: float | None = None + self.send_times: deque[float] = deque() def create_output_shm(self, name: str) -> None: out_shm = UntrackedSharedMemory(name=f"out-{name}", create=False) @@ -251,8 +238,8 @@ class AsyncDetectorRunner(FrigateProcess): logger.warning(f"Failed to get frame {connection_id} from SHM") continue - # send to accelerator (no per-send timestamp: inference speed is - # derived from result cadence, see class docstring) + # mark start time and send to accelerator + self.send_times.append(time.perf_counter()) assert self._detector is not None self._detector.async_send_input(input_frame, connection_id) @@ -266,6 +253,12 @@ class AsyncDetectorRunner(FrigateProcess): if connection_id is None: continue + if not self.send_times: + # guard; shouldn't happen if send/recv are balanced + continue + ts = self.send_times.popleft() + duration = time.perf_counter() - ts + # release input buffer assert self._frame_manager is not None self._frame_manager.close(connection_id) @@ -279,17 +272,8 @@ class AsyncDetectorRunner(FrigateProcess): assert self._publisher is not None self._publisher.publish(connection_id) - # update timers: per-frame processing time derived from result - # cadence (1000 / detection_fps). Idle gaps are not folded in, so - # a quiet stream never inflates the reported inference speed. - now = time.perf_counter() - if self._last_result_time is not None: - interval = now - self._last_result_time - if interval < self.ASYNC_RESULT_IDLE_INTERVAL: - self.avg_speed.value = ( - self.avg_speed.value * 9 + interval - ) / 10 - self._last_result_time = now + # update timers + self.avg_speed.value = (self.avg_speed.value * 9 + duration) / 10 self.start_time.value = 0.0 def run(self) -> None: