mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-13 02:02:48 +03:00
Improve System Health pane (#24188)
* build out system health pane * tweaks * fixes * fix notice link so it opens the correct camera * tweak language
This commit is contained in:
committed by
Nicolas Mowen
parent
6d33b31bc6
commit
70ce193e09
@@ -18,6 +18,36 @@ logger = logging.getLogger(__name__)
|
||||
# Process-wide lock serializing all OpenVINO compile/inference calls
|
||||
_OPENVINO_LOCK = threading.Lock()
|
||||
|
||||
# model file path -> (model type, device the runner actually loaded on); the
|
||||
# embeddings maintainer folds this per enrichment for the stats endpoint.
|
||||
# Models load on several threads (reindex, lazy first use), so writes and
|
||||
# snapshots go through the lock.
|
||||
loaded_devices: dict[str, tuple[str, str]] = {}
|
||||
_loaded_devices_lock = threading.Lock()
|
||||
|
||||
|
||||
def record_loaded_device(model_path: str, model_type: str, device: str) -> None:
|
||||
"""Record the device a model loaded on, for the enrichment stats."""
|
||||
with _loaded_devices_lock:
|
||||
loaded_devices[model_path] = (model_type, device)
|
||||
|
||||
logger.info("Loaded %s model on %s", model_type, device)
|
||||
|
||||
|
||||
def snapshot_loaded_devices() -> dict[str, tuple[str, str]]:
|
||||
"""A copy that is safe to iterate while other threads load models."""
|
||||
with _loaded_devices_lock:
|
||||
return dict(loaded_devices)
|
||||
|
||||
|
||||
_PROVIDER_LABELS = {
|
||||
"CUDAExecutionProvider": "CUDA",
|
||||
"TensorrtExecutionProvider": "TensorRT",
|
||||
"MIGraphXExecutionProvider": "MIGraphX",
|
||||
"OpenVINOExecutionProvider": "OpenVINO",
|
||||
"CPUExecutionProvider": "CPU",
|
||||
}
|
||||
|
||||
|
||||
def is_arm64_platform() -> bool:
|
||||
"""Check if we're running on an ARM platform."""
|
||||
@@ -117,6 +147,12 @@ class BaseModelRunner(ABC):
|
||||
"""Run inference with the model."""
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def device_name(self) -> str:
|
||||
"""Short label of the device the model actually loaded on."""
|
||||
pass
|
||||
|
||||
|
||||
class ONNXModelRunner(BaseModelRunner):
|
||||
"""Run ONNX models using ONNX Runtime."""
|
||||
@@ -174,6 +210,18 @@ class ONNXModelRunner(BaseModelRunner):
|
||||
|
||||
return self.ort.run(None, input)
|
||||
|
||||
@property
|
||||
def device_name(self) -> str:
|
||||
providers = self.ort.get_providers()
|
||||
|
||||
if not providers:
|
||||
return "CPU"
|
||||
|
||||
provider = providers[0]
|
||||
return _PROVIDER_LABELS.get(
|
||||
provider, provider.removesuffix("ExecutionProvider")
|
||||
)
|
||||
|
||||
|
||||
class CudaGraphRunner(BaseModelRunner):
|
||||
"""Encapsulates CUDA Graph capture and replay using ONNX Runtime IOBinding.
|
||||
@@ -261,6 +309,10 @@ class CudaGraphRunner(BaseModelRunner):
|
||||
self._session.run_with_iobinding(self._io_binding, ro)
|
||||
return self._io_binding.copy_outputs_to_cpu()
|
||||
|
||||
@property
|
||||
def device_name(self) -> str:
|
||||
return "CUDA"
|
||||
|
||||
|
||||
class OpenVINOModelRunner(BaseModelRunner):
|
||||
"""OpenVINO model runner that handles inference efficiently."""
|
||||
@@ -337,6 +389,8 @@ class OpenVINOModelRunner(BaseModelRunner):
|
||||
if device == "NPU" and OpenVINOModelRunner.is_detection_model(model_type):
|
||||
compile_config["NPU_TURBO"] = "YES"
|
||||
|
||||
self.compiled_device = device
|
||||
|
||||
# Compile model under the shared lock
|
||||
with _OPENVINO_LOCK:
|
||||
try:
|
||||
@@ -368,6 +422,22 @@ class OpenVINOModelRunner(BaseModelRunner):
|
||||
# model is complex and has dynamic shape
|
||||
pass
|
||||
|
||||
@property
|
||||
def device_name(self) -> str:
|
||||
device = self.compiled_device
|
||||
|
||||
if device == "AUTO":
|
||||
try:
|
||||
resolved = self.compiled_model.get_property("EXECUTION_DEVICES")
|
||||
|
||||
if resolved:
|
||||
device = ",".join(str(d) for d in resolved)
|
||||
except Exception:
|
||||
# older OpenVINO builds do not expose the property
|
||||
pass
|
||||
|
||||
return f"OpenVINO {device}"
|
||||
|
||||
def get_input_names(self) -> list[str]:
|
||||
"""Get input names for the model."""
|
||||
return [input.get_any_name() for input in self.compiled_model.inputs]
|
||||
@@ -515,6 +585,10 @@ class RKNNModelRunner(BaseModelRunner):
|
||||
logger.error(f"Error loading RKNN model: {e}")
|
||||
raise
|
||||
|
||||
@property
|
||||
def device_name(self) -> str:
|
||||
return "RKNN"
|
||||
|
||||
def get_input_names(self) -> list[str]:
|
||||
"""Get input names for the model."""
|
||||
# For detection models, we typically use "input" as the default input name
|
||||
@@ -595,6 +669,14 @@ class RKNNModelRunner(BaseModelRunner):
|
||||
pass
|
||||
|
||||
|
||||
def _record_runner(
|
||||
model_path: str, model_type: str, runner: BaseModelRunner
|
||||
) -> BaseModelRunner:
|
||||
"""Record the device a freshly loaded runner ended up on."""
|
||||
record_loaded_device(model_path, model_type, runner.device_name)
|
||||
return runner
|
||||
|
||||
|
||||
def get_optimized_runner(
|
||||
model_path: str, device: str | None, model_type: str, **kwargs
|
||||
) -> BaseModelRunner:
|
||||
@@ -605,7 +687,7 @@ def get_optimized_runner(
|
||||
rknn_path = auto_convert_model(model_path)
|
||||
|
||||
if rknn_path:
|
||||
return RKNNModelRunner(rknn_path)
|
||||
return _record_runner(model_path, model_type, RKNNModelRunner(rknn_path))
|
||||
|
||||
providers, options = get_ort_providers(device == "CPU", device, **kwargs)
|
||||
|
||||
@@ -614,7 +696,11 @@ def get_optimized_runner(
|
||||
# In other images we will get CUDA / ROCm which are preferred over OpenVINO
|
||||
# There is currently no way to prioritize OpenVINO over CUDA / ROCm in these images
|
||||
if device != "CPU" and is_openvino_gpu_npu_available():
|
||||
return OpenVINOModelRunner(model_path, device, model_type, **kwargs)
|
||||
return _record_runner(
|
||||
model_path,
|
||||
model_type,
|
||||
OpenVINOModelRunner(model_path, device, model_type, **kwargs),
|
||||
)
|
||||
|
||||
if (
|
||||
CudaGraphRunner.is_model_supported(model_type)
|
||||
@@ -624,13 +710,17 @@ def get_optimized_runner(
|
||||
**options[0],
|
||||
"enable_cuda_graph": True,
|
||||
}
|
||||
return CudaGraphRunner(
|
||||
ort.InferenceSession(
|
||||
model_path,
|
||||
providers=providers,
|
||||
provider_options=options,
|
||||
return _record_runner(
|
||||
model_path,
|
||||
model_type,
|
||||
CudaGraphRunner(
|
||||
ort.InferenceSession(
|
||||
model_path,
|
||||
providers=providers,
|
||||
provider_options=options,
|
||||
),
|
||||
options[0]["device_id"],
|
||||
),
|
||||
options[0]["device_id"],
|
||||
)
|
||||
|
||||
if (
|
||||
@@ -642,12 +732,16 @@ def get_optimized_runner(
|
||||
providers.pop(0)
|
||||
options.pop(0)
|
||||
|
||||
return ONNXModelRunner(
|
||||
ort.InferenceSession(
|
||||
model_path,
|
||||
sess_options=get_ort_session_options(model_type),
|
||||
providers=providers,
|
||||
provider_options=options,
|
||||
return _record_runner(
|
||||
model_path,
|
||||
model_type,
|
||||
ONNXModelRunner(
|
||||
ort.InferenceSession(
|
||||
model_path,
|
||||
sess_options=get_ort_session_options(model_type),
|
||||
providers=providers,
|
||||
provider_options=options,
|
||||
),
|
||||
model_type=model_type,
|
||||
),
|
||||
model_type=model_type,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user