From 1c9396449e49e6e48acd3e4e38ad10736f7f77aa Mon Sep 17 00:00:00 2001 From: Daniel-dev22 <47092714+Daniel-dev22@users.noreply.github.com> Date: Sat, 20 Jun 2026 09:57:01 -0400 Subject: [PATCH] perf(track): add __slots__ to TrackedObjectAttribute TrackedObjectAttribute is constructed per attribute (face, license plate, logo, ...) per frame in process_frames. It holds exactly six fixed fields and is never mutated beyond __init__, so __slots__ removes the per-instance __dict__. Measured in the release image: 352 B/instance (56 B object + 296 B __dict__) -> ~104 B with __slots__, ~70% smaller, on a high-churn per-frame object. Adds a test asserting no __dict__ and that undeclared attributes raise. Co-Authored-By: Claude Opus 4.8 (1M context) --- frigate/test/test_obects.py | 15 +++++++++++++++ frigate/track/tracked_object.py | 2 ++ 2 files changed, 17 insertions(+) diff --git a/frigate/test/test_obects.py b/frigate/test/test_obects.py index 8fe831980e..8ca11e3230 100644 --- a/frigate/test/test_obects.py +++ b/frigate/test/test_obects.py @@ -3,6 +3,21 @@ import unittest from frigate.track.tracked_object import TrackedObjectAttribute +class TestAttributeSlots(unittest.TestCase): + def test_attribute_uses_slots(self) -> None: + attribute = TrackedObjectAttribute( + ("amazon", 0.8, (847, 242, 883, 255), 468, 2.77, (702, 134, 1050, 482)) + ) + # __slots__ means no per-instance __dict__ + self.assertFalse(hasattr(attribute, "__dict__")) + # all declared fields are still populated and readable + self.assertEqual(attribute.label, "amazon") + self.assertEqual(attribute.region, (702, 134, 1050, 482)) + # setting an undeclared attribute must raise (catches accidental drift) + with self.assertRaises(AttributeError): + attribute.unexpected = 1 + + class TestAttribute(unittest.TestCase): def test_overlapping_object_selection(self) -> None: attribute = TrackedObjectAttribute( diff --git a/frigate/track/tracked_object.py b/frigate/track/tracked_object.py index 03117df692..555a8b89ac 100644 --- a/frigate/track/tracked_object.py +++ b/frigate/track/tracked_object.py @@ -574,6 +574,8 @@ def zone_filtered(obj: TrackedObject, object_config: dict[str, FilterConfig]) -> class TrackedObjectAttribute: + __slots__ = ("label", "score", "box", "area", "ratio", "region") + def __init__(self, raw_data: tuple) -> None: self.label = raw_data[0] self.score = raw_data[1]