mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-05 06:45:09 +00:00
perf: cheap per-frame and per-fetch savings (#725)
Six small savings with no behaviour change: the odds fetch no longer pretty-prints every response for a debug line; the scroll integer-slice path drops a redundant full-frame np.ascontiguousarray; ledmatrix-web.service gets MALLOC_ARENA_MAX=2 like the display unit; core ESPN responses are parsed via response_json (orjson when installed); and the scroll frame stats go to INFO only for degraded windows plus a 5-minute heartbeat. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,30 @@ import numpy as np
|
||||
# long over one frame, so a sample this large is an idle gap between scrolls.
|
||||
FPS_LOG_INTERVAL = 5.0
|
||||
|
||||
# The stats line goes to INFO only when a window is worth an operator's
|
||||
# attention, as Vegas's FPS line does (src/vegas_mode/coordinator.py): every
|
||||
# 5s from every scroller was most of the journal on a healthy rig. A window is
|
||||
# degraded when its frame rate falls below this fraction of the rate it was
|
||||
# locked to (1 / its own median frame time; same 0.9 as Vegas) ...
|
||||
STATS_HEALTHY_FRACTION = 0.9
|
||||
# ... or when more than this share of its frames stalled (past 1.5x the
|
||||
# median). A 1% stall rate barely moves the mean, so the fps test alone would
|
||||
# miss the judder this line exists to show.
|
||||
STATS_DEGRADED_STALL_RATE = 0.01
|
||||
# A healthy scroller still logs at INFO this often, so silence in the journal
|
||||
# means stopped rather than fine. Every window is still logged at DEBUG.
|
||||
STATS_HEARTBEAT_INTERVAL = 300.0
|
||||
|
||||
|
||||
def frame_stats_degraded(stats: Dict[str, Any]) -> bool:
|
||||
"""Whether one frame_stats() window is worth logging at INFO."""
|
||||
n = stats["frames"]
|
||||
if n == 0 or stats["median"] <= 0:
|
||||
return False
|
||||
locked_fps = 1.0 / stats["median"]
|
||||
return (stats["fps"] < locked_fps * STATS_HEALTHY_FRACTION
|
||||
or stats["stalls"] > n * STATS_DEGRADED_STALL_RATE)
|
||||
|
||||
|
||||
def _rgb_pixels(item) -> np.ndarray:
|
||||
"""An appended item's pixels as an RGB array, as pasting it would draw them."""
|
||||
@@ -189,6 +213,11 @@ class ScrollHelper:
|
||||
# Every frame time since the last stats line, so the 5s summary can
|
||||
# report the tail rather than one arbitrary sample. Cleared on log.
|
||||
self._window: list = []
|
||||
# INFO-level stats bookkeeping (see STATS_HEARTBEAT_INTERVAL). Kept
|
||||
# across reset_scroll(): a heartbeat per scroll start would bring the
|
||||
# chatter back. 0.0 so the first window after start-up is at INFO.
|
||||
self._stats_last_info_log = 0.0
|
||||
self._stats_was_degraded = False
|
||||
|
||||
# Scrolling state management
|
||||
self.is_scrolling = False
|
||||
@@ -573,9 +602,12 @@ class ScrollHelper:
|
||||
img_w = self.cached_array.shape[1]
|
||||
|
||||
if end_x <= img_w:
|
||||
# Normal case: single contiguous slice (fastest path)
|
||||
frame_array = np.ascontiguousarray(self.cached_array[:, start_x:end_x])
|
||||
return Image.frombytes('RGB', _size, frame_array.tobytes())
|
||||
# Normal case: single contiguous slice (fastest path). tobytes()
|
||||
# on the column-slice view already returns C-order bytes, so
|
||||
# ascontiguousarray() first only added a second full-frame copy.
|
||||
return Image.frombytes(
|
||||
'RGB', _size,
|
||||
self.cached_array[:, start_x:end_x].tobytes())
|
||||
else:
|
||||
# Ensure frame buffer is allocated for all non-simple paths
|
||||
if self._frame_buffer is None or self._frame_buffer.shape != (self.display_height, self.display_width, 3):
|
||||
@@ -1207,10 +1239,23 @@ class ScrollHelper:
|
||||
# as an idle gap. There is nothing to report, and reporting the
|
||||
# gap itself is the bug above.
|
||||
if self._window:
|
||||
self.logger.info(
|
||||
"Scroll frame stats - %s",
|
||||
format_frame_stats(self._window),
|
||||
)
|
||||
# INFO when degraded, on the window that recovers from it, and
|
||||
# as a slow heartbeat; DEBUG otherwise.
|
||||
degraded = frame_stats_degraded(frame_stats(self._window))
|
||||
if (degraded or self._stats_was_degraded
|
||||
or current_time - self._stats_last_info_log
|
||||
>= STATS_HEARTBEAT_INTERVAL):
|
||||
self.logger.info(
|
||||
"Scroll frame stats - %s",
|
||||
format_frame_stats(self._window),
|
||||
)
|
||||
self._stats_last_info_log = current_time
|
||||
elif self.logger.isEnabledFor(logging.DEBUG):
|
||||
self.logger.debug(
|
||||
"Scroll frame stats - %s",
|
||||
format_frame_stats(self._window),
|
||||
)
|
||||
self._stats_was_degraded = degraded
|
||||
self.last_fps_log_time = current_time
|
||||
self.frame_count = 0
|
||||
self._window = []
|
||||
|
||||
Reference in New Issue
Block a user