Revert "fix(object_detection): report per-frame time, not round-trip, for async detectors"

This reverts commit 92feefc113ca9fcb8c6d9d8e2a3c25730ee50208.
This commit is contained in:
patrykorwat 2026-08-23 17:30:02 +00:00
parent 92feefc113
commit 65a429ab33

View File

@ -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: