mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-10 09:06:36 +00:00
experiment(vegas): vegas_scroll.switch_interval_ms shortens the GIL switch interval during a run
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -82,6 +82,14 @@ class VegasModeConfig:
|
||||
# drawn off the render thread. See docs/OFFSCREEN_RENDERING.md.
|
||||
offscreen_prefetch: bool = True
|
||||
|
||||
# How long another thread may hold the GIL before the render thread's
|
||||
# request forces it to yield, in ms, while Vegas runs. CPython's default is
|
||||
# 5ms. Plugin rendering on the prefetch thread and plugin updates hold the
|
||||
# GIL in Pillow and Python code, and a frame waiting its turn for 5ms at a
|
||||
# time misses its refresh. 0 leaves the interpreter default alone.
|
||||
# Experimental: measured with scripts/frame_soak.py before it gets a default.
|
||||
switch_interval_ms: float = 0.0
|
||||
|
||||
# Keep one continuous strip, extending it with the next group of plugins as
|
||||
# the scroll approaches the end, instead of composing a fresh strip and
|
||||
# swapping it in. A swap stops the motion, substitutes every pixel at once
|
||||
@@ -212,6 +220,7 @@ class VegasModeConfig:
|
||||
sub_pixel_blend=bool(vegas_config.get('sub_pixel_blend', False)),
|
||||
continuous_scroll=vegas_config.get('continuous_scroll', True),
|
||||
offscreen_prefetch=bool(vegas_config.get('offscreen_prefetch', True)),
|
||||
switch_interval_ms=float(vegas_config.get('switch_interval_ms', 0.0) or 0.0),
|
||||
extend_threshold_screens=float(
|
||||
vegas_config.get('extend_threshold_screens', 2.0)),
|
||||
auto_trim=vegas_config.get('auto_trim', True),
|
||||
@@ -254,6 +263,7 @@ class VegasModeConfig:
|
||||
'sub_pixel_blend': self.sub_pixel_blend,
|
||||
'continuous_scroll': self.continuous_scroll,
|
||||
'offscreen_prefetch': self.offscreen_prefetch,
|
||||
'switch_interval_ms': self.switch_interval_ms,
|
||||
'extend_threshold_screens': self.extend_threshold_screens,
|
||||
'auto_trim': self.auto_trim,
|
||||
'trim_threshold': self.trim_threshold,
|
||||
|
||||
@@ -13,6 +13,7 @@ Supports three display modes per plugin:
|
||||
|
||||
import logging
|
||||
import math
|
||||
import sys
|
||||
import time
|
||||
import threading
|
||||
from typing import Optional, Dict, Any, List, Callable, TYPE_CHECKING
|
||||
@@ -280,6 +281,7 @@ class VegasModeCoordinator:
|
||||
# due immediately so the first sample confirms the marquee is up.
|
||||
self._fps_last_health_log = 0.0
|
||||
self._fps_was_degraded = False
|
||||
self._apply_switch_interval()
|
||||
|
||||
# Line up the next group immediately, so the first extension is already
|
||||
# warm rather than stalling the scroll to fetch it.
|
||||
@@ -302,6 +304,8 @@ class VegasModeCoordinator:
|
||||
self.stats['total_runtime_seconds'] += time.time() - self._start_time
|
||||
self._start_time = None
|
||||
|
||||
self._restore_switch_interval()
|
||||
|
||||
# Cleanup components
|
||||
self.render_pipeline.reset()
|
||||
self.stream_manager.reset()
|
||||
@@ -309,6 +313,23 @@ class VegasModeCoordinator:
|
||||
|
||||
logger.info("Vegas mode stopped")
|
||||
|
||||
def _apply_switch_interval(self) -> None:
|
||||
"""Shorten the GIL switch interval for the run; see VegasModeConfig."""
|
||||
ms = self.vegas_config.switch_interval_ms
|
||||
if not ms or ms <= 0:
|
||||
return
|
||||
if getattr(self, '_saved_switch_interval', None) is None:
|
||||
self._saved_switch_interval = sys.getswitchinterval()
|
||||
sys.setswitchinterval(ms / 1000.0)
|
||||
logger.info("Vegas: GIL switch interval %.1fms (was %.1fms)",
|
||||
ms, self._saved_switch_interval * 1000.0)
|
||||
|
||||
def _restore_switch_interval(self) -> None:
|
||||
saved = getattr(self, '_saved_switch_interval', None)
|
||||
if saved is not None:
|
||||
sys.setswitchinterval(saved)
|
||||
self._saved_switch_interval = None
|
||||
|
||||
def pause(self) -> None:
|
||||
"""Pause Vegas mode (for live priority interruption)."""
|
||||
with self._state_lock:
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
"""vegas_scroll.switch_interval_ms: applied for a Vegas run, restored after."""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
from src.vegas_mode.config import VegasModeConfig # noqa: E402
|
||||
from src.vegas_mode.coordinator import VegasModeCoordinator # noqa: E402
|
||||
|
||||
|
||||
def _coordinator(ms):
|
||||
c = VegasModeCoordinator.__new__(VegasModeCoordinator)
|
||||
c.vegas_config = VegasModeConfig(switch_interval_ms=ms)
|
||||
return c
|
||||
|
||||
|
||||
def test_applied_for_the_run_and_restored_after():
|
||||
before = sys.getswitchinterval()
|
||||
c = _coordinator(1.0)
|
||||
try:
|
||||
c._apply_switch_interval()
|
||||
assert abs(sys.getswitchinterval() - 0.001) < 1e-9
|
||||
c._apply_switch_interval() # a second start must not lose the original
|
||||
finally:
|
||||
c._restore_switch_interval()
|
||||
assert sys.getswitchinterval() == before
|
||||
|
||||
|
||||
def test_zero_leaves_the_interpreter_alone():
|
||||
before = sys.getswitchinterval()
|
||||
c = _coordinator(0.0)
|
||||
c._apply_switch_interval()
|
||||
c._restore_switch_interval()
|
||||
assert sys.getswitchinterval() == before
|
||||
|
||||
|
||||
def test_read_from_config():
|
||||
config = VegasModeConfig.from_config(
|
||||
{"display": {"vegas_scroll": {"switch_interval_ms": 1}}})
|
||||
assert config.switch_interval_ms == 1.0
|
||||
assert VegasModeConfig.from_config({"display": {"vegas_scroll": {}}}).switch_interval_ms == 0.0
|
||||
Reference in New Issue
Block a user