RT performance tuning: IRQ affinity, chrt, xrun tracking, reference doc
CI / test (push) Has been cancelled
CI / test (push) Has been cancelled
- Add USB audio IRQ affinity pinning to core 3 in main.py - Add enable_xrun_tracking() to AudioSystem for kernel-level diagnostics - Wrap Python process with chrt -f 80 in systemd service template - Add LimitSIGPENDING=128 for signal queue depth - Create scripts/rt-tune.sh — comprehensive RT tuning startup script (IRQ affinity, CPU governor, C-states, ALSA limits, xrun_debug) - Create docs/rt-performance-tuning.md — reference doc with all tuning knobs, measurement tools, and systematic procedure Targets: <12ms RT latency (8ms ideal), zero xruns, CPU <40% at 512/48k
This commit is contained in:
@@ -194,6 +194,12 @@ class PedalApp:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Enable kernel-level xrun tracking for diagnostics
|
||||
try:
|
||||
self.audio_system.enable_xrun_tracking()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if self.audio_config.jack_enabled:
|
||||
self.audio_system.start_jack(timeout=10)
|
||||
else:
|
||||
@@ -839,6 +845,48 @@ def main() -> int:
|
||||
except Exception as exc:
|
||||
logger.warning("Could not set CPU governor (non-root?): %s", exc)
|
||||
|
||||
# Pin USB audio IRQ to a dedicated core for stable RT audio
|
||||
# On RPi 4B, cores 0-2 handle kernel/general interrupts;
|
||||
# pinning USB audio to core 3 isolates it from that noise.
|
||||
try:
|
||||
import glob as _glob
|
||||
# Find the USB audio interface IRQ
|
||||
_usb_irq: str | None = None
|
||||
# Strategy 1: xhci-hcd (USB 3.0 controller on Pi 4B)
|
||||
for _proc_dir in _glob.glob("/proc/irq/[0-9]*"):
|
||||
try:
|
||||
_name = (_glob.glob(f"{_proc_dir}/name")[0] if _glob.glob(f"{_proc_dir}/name") else None)
|
||||
if _name:
|
||||
_irq_name = open(_name).read().strip()
|
||||
if "xhci" in _irq_name or "dwc" in _irq_name:
|
||||
_usb_irq = os.path.basename(_proc_dir)
|
||||
break
|
||||
except (OSError, PermissionError, IndexError):
|
||||
continue
|
||||
if _usb_irq:
|
||||
# Pin to core 3 (smp_affinity mask = 0x8)
|
||||
_aff_path = f"/proc/irq/{_usb_irq}/smp_affinity"
|
||||
_aff_list_path = f"/proc/irq/{_usb_irq}/smp_affinity_list"
|
||||
with open(_aff_path, "w") as f:
|
||||
f.write("8")
|
||||
with open(_aff_list_path, "w") as f:
|
||||
f.write("3")
|
||||
# Move all other IRQs away from core 3
|
||||
for _proc_dir in _glob.glob("/proc/irq/[0-9]*"):
|
||||
_irq_num = os.path.basename(_proc_dir)
|
||||
if _irq_num == _usb_irq:
|
||||
continue # Skip our USB audio IRQ
|
||||
try:
|
||||
with open(f"{_proc_dir}/smp_affinity", "w") as f:
|
||||
f.write("7") # cores 0,1,2
|
||||
except (OSError, PermissionError):
|
||||
continue
|
||||
logger.info("USB audio IRQ %s pinned to core 3 for RT stability", _usb_irq)
|
||||
else:
|
||||
logger.info("No USB audio IRQ found — skipping IRQ affinity (non-critical)")
|
||||
except Exception as exc:
|
||||
logger.warning("Could not set IRQ affinity (non-root?): %s", exc)
|
||||
|
||||
# Disable Python garbage collector to prevent 10-50ms GC pauses in
|
||||
# the real-time audio callback. At ~500 numpy allocs/sec in the
|
||||
# pipeline, default GC (threshold=700) triggers every ~1.4s,
|
||||
|
||||
Reference in New Issue
Block a user