Pipes in Linux und CPU-Affinitäten
Verfasst: 01.06.2020, 13:51
Hallo zusammen,
seit einiger Zeit setze ich einen RT Kernel ein und die Audiowiedergabe läuft über kodi und eine nachfolgende pipe in der Art: kodi(dec) > sox(i2f) > resample_soxr(src) > brutefir(drc) > sox(f2i) > sox/playhrt(write):
- kodi(dec): Audioformat nach PCM dekodieren
- sox(i2f): int nach float64
- resample_soxr(src): SampleRateConversion
- brutefir(drc): Raumkorrektur
- sox(f2i): float64 nach int (incl. Dithering)
- sox/playhrt(write): Schreiben auf das alsa output device
Pipe 1: resample_soxr/brutefir/sox auf demselben Core
Leider habe ich immer wieder (sehr) sporadisch Aussetzer oder Klicks gehört. Diese wurden vermutlich durch buffer underruns verursacht, die ich leider nur zum Teil angezeigt bekam. Ich habe mir deswegen in den letzten Tagen noch einmal die jeweilige Priorität und CPU-Affinität der einzelnen Prozesse angeschaut, und auch mittels cyclictest die Latenzen von Testprozessen gemessen. Diese Testprozesse haben eine geringere oder gleiche Priorität wie die signalverarbeitenden Prozesse der Audiopipe -- also sox, resample_soxr, brutefir und playhrt. Auf Basis dieser Messungen kann man sehen wie lange ein Prozess auf einem CPU-Core wartet bis er an die Reihe kommt. Interessanterweise sehe ich, dass brutefir und resample_soxr in meiner Konfiguration bis zu mehreren tausend µs den jeweiligen CPU-Core blockieren. Dabei blockiert resample_soxr bis maximal etwa 5.000 µs und brutefir bis maximal etwa 4.000 µs (Bild 1). Wenn beide auf auf dem gleichen Core laufen, werden andere Prozesse bis zu >15.000 µs blockiert. Sprich: Die formatwandelnden sox-Prozesse, die die Daten am Eingang und Ausgang der pipe verarbeiten, können bis zu mehr als 15 ms verzögert werden.
Bild 1: Latenzen 0-8.000 µs (Core 0: RT tasks, Core 1: resample_soxr, Core 2: brutefir/sox, Core 3: playhrt)
In meiner bisherigen Audiopipeline liefen brutefir, resample_soxr sowie die formatwandelnden sox-Prozesse alle auf demselben Core, und nur kodi und der in das alsa-device schreibende Prozess liefen jeweils auf einem eigenen Core (Pipe 1). Ich gehe davon aus, dass im worst-case die pipeline durchaus in buffer underruns ging, und habe jetzt die CPU-Affinität so angepasst, dass brutefir und resample_soxr auf unterschiedlichen CPU-Cores laufen (Pipe 2). Damit werden die formatwandelnden sox-Prozesse um maximal 4.000 µs aufgehalten. Das wiederum sollte über die generell eingesetzten Pufferlängen abgefangen werden. Ich werde mir das jetzt eine Weile anhören und abwarten, ob ich immer noch die sporadischen Aussetzer und Klicks erlebe.
Pipe 2: resample_soxr auf anderem Core als brutefir/sox
Auch interessant ist der Unterschied von sox und playhrt beim Schreiben auf das alsa-device. Während sox andere Prozesse auf demselben den Core bis zu 500 µs warten lässt (Bild 2), reduziert resample_soxr das auf maximal 40-50 µs (Bild 3).
Bild 2: Latenzen 0-8.000 µs(Core 0: RT tasks, Core 1: resample_soxr, Core 2: brutefir/sox, Core 3: soxplay)
Bild 3: Latenzen 0-400 µs (Core 0: RT tasks, Core 1: resample_soxr, Core 2: brutefir/sox, Core 3: playhrt)
Tja, jetzt merke ich, dass meine CPU zu wenig Cores hat. Gerne würde ich eigene Cores für kodi (1), sox-Formatwandlung (2), SRC (3), DRC (4), alsa-write (5) und noch einige Cores für das OS spendieren. Das ruft nach einer 8-Core CPU.
Viele Grüße,
Andree
seit einiger Zeit setze ich einen RT Kernel ein und die Audiowiedergabe läuft über kodi und eine nachfolgende pipe in der Art: kodi(dec) > sox(i2f) > resample_soxr(src) > brutefir(drc) > sox(f2i) > sox/playhrt(write):
- kodi(dec): Audioformat nach PCM dekodieren
- sox(i2f): int nach float64
- resample_soxr(src): SampleRateConversion
- brutefir(drc): Raumkorrektur
- sox(f2i): float64 nach int (incl. Dithering)
- sox/playhrt(write): Schreiben auf das alsa output device
Code: Alles auswählen
pcm.pipe_src96k_drc_dither_playhrt {
type file
file " | chrt -f 80 taskset -c 2 sox -D -v 0.8 -r 44.1k -b 32 -c 2 -e signed -t raw - -b 64 -c 2 -e float -t raw - \
| chrt -f 81 taskset -c 2 resample_soxr -c 2 -B 89 -P 50 -e 28 -i 44100 -o 96000 \
| chrt -f 82 taskset -c 2 brutefir -quiet -nodefault /home/buschel/Convolver/DRC_96k_stdio.conf \
| chrt -f 83 taskset -c 2 sox -t raw -r 96k -b 64 -c 2 -e float - -b 32 -c 2 -e signed -t raw - dither -p 24 \
| chrt -f 90 taskset -c 3 playhrt --stdin -k 2 -s 96000 -f S32_LE -d usbaudio --mmap --verbose --sleep=1000000"
format "raw"
}
Leider habe ich immer wieder (sehr) sporadisch Aussetzer oder Klicks gehört. Diese wurden vermutlich durch buffer underruns verursacht, die ich leider nur zum Teil angezeigt bekam. Ich habe mir deswegen in den letzten Tagen noch einmal die jeweilige Priorität und CPU-Affinität der einzelnen Prozesse angeschaut, und auch mittels cyclictest die Latenzen von Testprozessen gemessen. Diese Testprozesse haben eine geringere oder gleiche Priorität wie die signalverarbeitenden Prozesse der Audiopipe -- also sox, resample_soxr, brutefir und playhrt. Auf Basis dieser Messungen kann man sehen wie lange ein Prozess auf einem CPU-Core wartet bis er an die Reihe kommt. Interessanterweise sehe ich, dass brutefir und resample_soxr in meiner Konfiguration bis zu mehreren tausend µs den jeweiligen CPU-Core blockieren. Dabei blockiert resample_soxr bis maximal etwa 5.000 µs und brutefir bis maximal etwa 4.000 µs (Bild 1). Wenn beide auf auf dem gleichen Core laufen, werden andere Prozesse bis zu >15.000 µs blockiert. Sprich: Die formatwandelnden sox-Prozesse, die die Daten am Eingang und Ausgang der pipe verarbeiten, können bis zu mehr als 15 ms verzögert werden.
Bild 1: Latenzen 0-8.000 µs (Core 0: RT tasks, Core 1: resample_soxr, Core 2: brutefir/sox, Core 3: playhrt)
In meiner bisherigen Audiopipeline liefen brutefir, resample_soxr sowie die formatwandelnden sox-Prozesse alle auf demselben Core, und nur kodi und der in das alsa-device schreibende Prozess liefen jeweils auf einem eigenen Core (Pipe 1). Ich gehe davon aus, dass im worst-case die pipeline durchaus in buffer underruns ging, und habe jetzt die CPU-Affinität so angepasst, dass brutefir und resample_soxr auf unterschiedlichen CPU-Cores laufen (Pipe 2). Damit werden die formatwandelnden sox-Prozesse um maximal 4.000 µs aufgehalten. Das wiederum sollte über die generell eingesetzten Pufferlängen abgefangen werden. Ich werde mir das jetzt eine Weile anhören und abwarten, ob ich immer noch die sporadischen Aussetzer und Klicks erlebe.
Code: Alles auswählen
pcm.pipe_src96k_drc_dither_playhrt {
type file
file " | chrt -f 80 taskset -c 2 sox -D -v 0.8 -r 44.1k -b 32 -c 2 -e signed -t raw - -b 64 -c 2 -e float -t raw - \
| chrt -f 81 taskset -c 1 resample_soxr -c 2 -B 89 -P 50 -e 28 -i 44100 -o 96000 \
| chrt -f 82 taskset -c 2 brutefir -quiet -nodefault /home/buschel/Convolver/DRC_96k_stdio.conf \
| chrt -f 83 taskset -c 2 sox -t raw -r 96k -b 64 -c 2 -e float - -b 32 -c 2 -e signed -t raw - dither -p 24 \
| chrt -f 90 taskset -c 3 playhrt --stdin -k 2 -s 96000 -f S32_LE -d usbaudio --mmap --verbose --sleep=1000000"
format "raw"
}
Auch interessant ist der Unterschied von sox und playhrt beim Schreiben auf das alsa-device. Während sox andere Prozesse auf demselben den Core bis zu 500 µs warten lässt (Bild 2), reduziert resample_soxr das auf maximal 40-50 µs (Bild 3).
Bild 2: Latenzen 0-8.000 µs(Core 0: RT tasks, Core 1: resample_soxr, Core 2: brutefir/sox, Core 3: soxplay)
Bild 3: Latenzen 0-400 µs (Core 0: RT tasks, Core 1: resample_soxr, Core 2: brutefir/sox, Core 3: playhrt)
Tja, jetzt merke ich, dass meine CPU zu wenig Cores hat. Gerne würde ich eigene Cores für kodi (1), sox-Formatwandlung (2), SRC (3), DRC (4), alsa-write (5) und noch einige Cores für das OS spendieren. Das ruft nach einer 8-Core CPU.
Viele Grüße,
Andree