RT performance tuning: IRQ affinity, chrt, xrun tracking, reference doc
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:
2026-06-20 15:58:54 -04:00
parent 04931fd738
commit 3f7257aa23
5 changed files with 895 additions and 2 deletions
+48
View File
@@ -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,