mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-18 04:49:13 +00:00
perf(harness): share one cache across a plugin's renders
_instantiate built a fresh MockCacheManager for every (size, mode), and that mock is a per-instance in-memory dict, so each render was a cold start. A plugin that fetches per game or per player re-fetched everything N times over -- baseball-scoreboard at one size took 840s for nine renders where the arithmetic said ~72s, and at eight sizes it exceeded a 900s timeout. The second and later renders also never exercised the cache-hit path, which is what a running rig executes almost all of the time, so a caching regression could not be caught here. The cache is now built once per render_plugin_matrix call and threaded down. The display manager stays per-render -- the bounds checking depends on that -- so only fetched data is shared. Measured on the rig, same render counts and same goldens: tide-display 2s -> 1s (32 renders) cricket-scoreboard 10s -> 3s (24 renders) No pass/fail change across tide-display, cricket-scoreboard, clock-simple, geochron, christmas-countdown, of-the-day, web-ui-info and incoming-packages. Closes #533.
This commit is contained in:
@@ -116,14 +116,23 @@ def list_modes(plugin_instance: Any, manifest: Dict[str, Any], plugin_id: str) -
|
|||||||
|
|
||||||
def _instantiate(plugin_id: str, manifest: Dict[str, Any], plugin_dir: Path,
|
def _instantiate(plugin_id: str, manifest: Dict[str, Any], plugin_dir: Path,
|
||||||
config: Dict[str, Any], mock_data: Dict[str, Any],
|
config: Dict[str, Any], mock_data: Dict[str, Any],
|
||||||
display_manager: Any) -> Any:
|
display_manager: Any, cache_manager: Any = None) -> Any:
|
||||||
"""Load and construct a plugin instance with mocked managers."""
|
"""Load and construct a plugin instance with mocked managers.
|
||||||
|
|
||||||
|
Pass ``cache_manager`` to share one cache across the renders of a plugin.
|
||||||
|
Building a fresh one per (size, mode) made every render a cold start, so a
|
||||||
|
plugin that fetches per game or per player re-fetched everything N times --
|
||||||
|
baseball-scoreboard took 840s for nine renders where ~72s was the arithmetic
|
||||||
|
-- and the cache-hit path, which is what a running rig executes almost
|
||||||
|
always, was never exercised.
|
||||||
|
"""
|
||||||
from src.plugin_system.plugin_loader import PluginLoader
|
from src.plugin_system.plugin_loader import PluginLoader
|
||||||
from src.plugin_system.testing import MockCacheManager, MockPluginManager
|
from src.plugin_system.testing import MockCacheManager, MockPluginManager
|
||||||
|
|
||||||
cache_manager = MockCacheManager()
|
if cache_manager is None:
|
||||||
for key, value in (mock_data or {}).items():
|
cache_manager = MockCacheManager()
|
||||||
cache_manager.set(key, value)
|
for key, value in (mock_data or {}).items():
|
||||||
|
cache_manager.set(key, value)
|
||||||
|
|
||||||
loader = PluginLoader()
|
loader = PluginLoader()
|
||||||
plugin_instance, _module = loader.load_plugin(
|
plugin_instance, _module = loader.load_plugin(
|
||||||
@@ -202,25 +211,35 @@ def render_plugin_matrix(
|
|||||||
# rendering a smaller one, instead of being clipped into a false pass.
|
# rendering a smaller one, instead of being clipped into a false pass.
|
||||||
extent = (max(w for w, _ in sizes), max(h for _, h in sizes))
|
extent = (max(w for w, _ in sizes), max(h for _, h in sizes))
|
||||||
|
|
||||||
|
# One cache for the whole matrix: see _instantiate. The display manager
|
||||||
|
# stays per-render (the bounds checking depends on that); only fetched data
|
||||||
|
# is shared.
|
||||||
|
from src.plugin_system.testing import MockCacheManager
|
||||||
|
cache_manager = MockCacheManager()
|
||||||
|
for key, value in (mock_data or {}).items():
|
||||||
|
cache_manager.set(key, value)
|
||||||
|
|
||||||
with _freeze(freeze_time):
|
with _freeze(freeze_time):
|
||||||
for width, height in sizes:
|
for width, height in sizes:
|
||||||
results.extend(_render_size(
|
results.extend(_render_size(
|
||||||
plugin_id, manifest, plugin_dir, config, mock_data or {},
|
plugin_id, manifest, plugin_dir, config, mock_data or {},
|
||||||
width, height, run_update, extent,
|
width, height, run_update, extent, cache_manager,
|
||||||
))
|
))
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def _render_size(plugin_id, manifest, plugin_dir, config, mock_data,
|
def _render_size(plugin_id, manifest, plugin_dir, config, mock_data,
|
||||||
width, height, run_update, extent) -> List[RenderResult]:
|
width, height, run_update, extent,
|
||||||
|
cache_manager=None) -> List[RenderResult]:
|
||||||
"""Render every mode at one size. A fresh instance per mode avoids state leaks."""
|
"""Render every mode at one size. A fresh instance per mode avoids state leaks."""
|
||||||
results: List[RenderResult] = []
|
results: List[RenderResult] = []
|
||||||
|
|
||||||
# Discover modes once per size (instance build can depend on config).
|
# Discover modes once per size (instance build can depend on config).
|
||||||
try:
|
try:
|
||||||
probe_dm = BoundsCheckingDisplayManager(width=width, height=height, overflow_extent=extent)
|
probe_dm = BoundsCheckingDisplayManager(width=width, height=height, overflow_extent=extent)
|
||||||
probe = _instantiate(plugin_id, manifest, plugin_dir, config, mock_data, probe_dm)
|
probe = _instantiate(plugin_id, manifest, plugin_dir, config, mock_data, probe_dm,
|
||||||
|
cache_manager)
|
||||||
modes = list_modes(probe, manifest, plugin_id)
|
modes = list_modes(probe, manifest, plugin_id)
|
||||||
except Exception as e: # noqa: BLE001 — surface any load failure as a result
|
except Exception as e: # noqa: BLE001 — surface any load failure as a result
|
||||||
return [RenderResult(plugin_id, width, height, "<load>", error=repr(e))]
|
return [RenderResult(plugin_id, width, height, "<load>", error=repr(e))]
|
||||||
@@ -229,7 +248,8 @@ def _render_size(plugin_id, manifest, plugin_dir, config, mock_data,
|
|||||||
result = RenderResult(plugin_id, width, height, mode)
|
result = RenderResult(plugin_id, width, height, mode)
|
||||||
dm = BoundsCheckingDisplayManager(width=width, height=height, overflow_extent=extent)
|
dm = BoundsCheckingDisplayManager(width=width, height=height, overflow_extent=extent)
|
||||||
try:
|
try:
|
||||||
inst = _instantiate(plugin_id, manifest, plugin_dir, config, mock_data, dm)
|
inst = _instantiate(plugin_id, manifest, plugin_dir, config, mock_data, dm,
|
||||||
|
cache_manager)
|
||||||
if run_update:
|
if run_update:
|
||||||
try:
|
try:
|
||||||
inst.update()
|
inst.update()
|
||||||
|
|||||||
Reference in New Issue
Block a user