mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-05 06:45:09 +00:00
perf(display): skip preview work nobody reads (lazy checksum, 1 Hz snapshot writer) (#717)
Mid-scroll, update_display() no longer checksums every frame: it asks is_currently_scrolling() once per frame and reuses the answer, hashes only when dirty tracking can skip a static frame, and the preview snapshot asks its policy first and hashes only when a write or touch could follow (decide() is monotone, pinned by a property test). With the preview open the snapshot is written at most once a second (VIEWER_INTERVAL 1.0 s, was 0.2 s; the SSE stream re-read it once a second, so four encodes in five went unread); the stream now polls its mtime every 0.25 s (VIEWER_POLL_INTERVAL), so the preview stays about as fresh. The PNG is written at compress_level=1. --preview soaks are not comparable across this change. Merged with #716: _scan_segments takes the frame's one scrolling answer and #716's static-handover pass-through, as soaked on ledpi (A B B A, 20 min each: main 0.118% / 0.113% late, with #716 and this 0.107% / 0.104%). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,9 @@ The invariants that matter:
|
||||
frames at 5 fps, 24/7)
|
||||
- the file mtime never goes stale enough to trip the health check's 60s
|
||||
degraded threshold (api_v3 get_hardware_status)
|
||||
- a viewer gets full cadence; no viewer drops to the idle keepalive
|
||||
- a viewer gets the viewer cadence; no viewer drops to the idle keepalive
|
||||
- decide() is monotone in frame_changed, which DisplayManager's lazy
|
||||
frame hash relies on
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -19,6 +21,8 @@ from src.common.snapshot_policy import ( # noqa: E402
|
||||
IDLE_INTERVAL,
|
||||
TOUCH_INTERVAL,
|
||||
VIEWER_INTERVAL,
|
||||
VIEWER_MARKER_FRESH_SEC,
|
||||
VIEWER_POLL_INTERVAL,
|
||||
SnapshotAction,
|
||||
decide,
|
||||
)
|
||||
@@ -89,5 +93,45 @@ class TestHealthKeepalive:
|
||||
assert action in (SnapshotAction.WRITE, SnapshotAction.TOUCH)
|
||||
|
||||
|
||||
class TestMonotoneInFrameChanged:
|
||||
"""DisplayManager asks decide() as if the frame had changed, and only
|
||||
hashes the frame when that answer is not SKIP. That is exact only while
|
||||
a SKIP for a changed frame is also a SKIP for an unchanged one."""
|
||||
|
||||
def test_skip_for_a_changed_frame_is_skip_for_an_unchanged_one(self):
|
||||
# Ages that straddle every threshold decide() compares against, plus
|
||||
# a timestamp in the future (a wall-clock step back) and "never".
|
||||
eps = 1e-6
|
||||
ages = sorted({0.0, -1.0, 1000.0, 100.0} | {
|
||||
base + delta
|
||||
for base in (VIEWER_INTERVAL, IDLE_INTERVAL, TOUCH_INTERVAL)
|
||||
for delta in (-eps, 0.0, eps)
|
||||
})
|
||||
now = 1000.0
|
||||
checked = 0
|
||||
for write_age in ages:
|
||||
for touch_age in ages:
|
||||
for viewer in (True, False):
|
||||
args = (now, now - write_age, now - touch_age, viewer)
|
||||
if decide(*args, frame_changed=True) is SnapshotAction.SKIP:
|
||||
checked += 1
|
||||
assert decide(*args, frame_changed=False) is SnapshotAction.SKIP, args
|
||||
assert checked, "the grid never reached a SKIP; it tests nothing"
|
||||
|
||||
|
||||
class TestPreviewPolling:
|
||||
def test_the_reader_polls_faster_than_the_writer_writes(self):
|
||||
"""Equal periods alias: two unsynchronised clocks of the same period
|
||||
leave the preview up to a period stale, with the odd double gap.
|
||||
Polling at least twice per interval keeps each frame at most half an
|
||||
interval late."""
|
||||
assert VIEWER_POLL_INTERVAL <= VIEWER_INTERVAL / 2
|
||||
|
||||
def test_polling_keeps_the_viewer_marker_fresh(self):
|
||||
# The SSE reader touches the marker about once a second however fast
|
||||
# it polls; the display must still count that as a live viewer.
|
||||
assert max(VIEWER_POLL_INTERVAL, 1.0) < VIEWER_MARKER_FRESH_SEC
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(pytest.main([__file__, "-v"]))
|
||||
|
||||
Reference in New Issue
Block a user