Alsa vs. Tinyalsa: Some experiments in performance and latency

In the interest of getting the lowest possible audio latency out of my x86_64 Lakka box, I decided to see if I could determine which of the audio drivers available was the most performant in this regard. The results so far are interesting, but a bit baffling to me all the same, especially since there’s nowhere near as much documentation on the audio drivers and how they perform as I would like, so feedback on this would be quite welcome.

So first things first: I installed Lakka on a generic Dell PC running a Core i5-7500 CPU running at 3.4 GHz, paired with a cheap Radeon R7 250 GPU. I am outputting sound through the onboard audio. I usually set the video driver to vulkan with Vsync off and max swapchain images set to 2, turn on Run-ahead and Automatic Frame Delay, and throw a light CRT shader on top (usually crt-easymode-halation).

As for audio, Lakka only offers the following audio drivers to me: alsa, alsathread, tinyalsa, and oss. Alsathread has been extremely unperformant for me, requiring a latency setting of around 128 ms to prevent stutter. OSS straight-up fails to initialize and I get no audio. That leaves alsa and tinyalsa to compare. The game I used to test everything is Yoshi’s Island using the latest Snes9x core.

When I first installed Lakka, not only was alsathread (the default driver, by the way) not giving me good results, but neither was alsa for some reason. Both would cause stutter and slowdown like crazy. Tinyalsa worked right off the bat, and after a look at the logs, I learned the minimum latency setting it accepts is 21 ms (going below that makes it set itself back up to 64 ms automatically), so I basically set it and forget it, along with the rest of the settings I outlined above.

However, I decided to try to revisit alsa and see why I was having so much trouble with it. Sure enough, using the 21 ms value I had it at, I immediately got slowdown and stutter, but I began trying a few things to see if I could improve the situation. Disabling frame delay, run-ahead and shaders did little, so instead I tried re-enabling those and just upping swapchain images up to 3, and that did the trick for the most part, although I also ended up having to disable frame delay to remove some lingering microstutters. Then I thought, if I raise audio latency instead, can I keep swapchain images at 2 and keep the rest of the goodies as well? Sure enough, raising it upwards of 64 ms did the trick. After a lot of trial and error, I discovered a threshold: as long as audio latency is set to 54 ms or above, I can keep all my latency reduction settings, plus shaders, intact. But as soon as I go below that, the stuttering begins. I tried all this again with the gl and glcore drivers, and I got the same results (well, for some reason I had to enable Vsync using glcore to prevent jitter; gl and vulkan can do without it, apparently)

So what is going on here? Why does tinyalsa allow me to go as low as 21ms, but alsa has to hover around 54? I decided to look at the logs, and I found something curious. When tinyalsa is used and set to 21 ms, this is what it outputs:

[INFO] [TINYALSA]: Using card: 0, device: 0. [INFO] [TINYALSA]: Can pause: yes. [INFO] [TINYALSA]: Audio rate: 48000Hz. [INFO] [TINYALSA]: Buffer size: 4096 frames. [INFO] [TINYALSA]: Buffer size: 16384 bytes. [INFO] [TINYALSA]: Frame size: 4 bytes. [INFO] [TINYALSA]: Latency: 21ms.

However, when set to 54 ms, this is the output:

[INFO] [TINYALSA]: Using card: 0, device: 0. [INFO] [TINYALSA]: Can pause: yes. [INFO] [TINYALSA]: Audio rate: 48000Hz. [INFO] [TINYALSA]: Buffer size: 10432 frames. [INFO] [TINYALSA]: Buffer size: 16384 bytes. [INFO] [TINYALSA]: Frame size: 4 bytes. [INFO] [TINYALSA]: Latency: 54ms.

That’s odd. It mentions two buffer sizes (one in frames, the other in bytes) each time, but only one of them changes. Now, the key here appears to be the frame size of 4 bytes. Sure enough, when set to 21 ms, the given buffer size of 4096 frames times 4 bytes gives you 16384 bytes. But even though the buffer size in frames increases when set to 54 ms, the buffer size in bytes stays the same!

This gave me an idea. When I was first trying out things with alsa, at one point I had tried setting it to the max latency of 512 ms, and it resulted in ridiculous audio delay, so much even the most casual observer ought to be able to notice it. I tried doing the same with tinyalsa, and, wouldn’t you know it, it sounds virtually identical to setting it to 21 ms! Is the audio latency setting even doing anything with tinyalsa, then?

So, what does alsa report on its log? Well, it’s pretty interesting. This is what it looks like when set to 53 ms, that is, the threshold where it begins to stutter:

[INFO] [Audio]: Set audio input rate to: 31987.32 Hz. [INFO] [ALSA]: Using floating point format. [INFO] [ALSA]: Period size: 1024 frames [INFO] [ALSA]: Buffer size: 2048 frames [INFO] [ALSA]: Can pause: no.

Raising it to 54 ms causes the following:

[INFO] [Audio]: Set audio input rate to: 31987.32 Hz. [INFO] [ALSA]: Using floating point format. [INFO] [ALSA]: Period size: 1024 frames [INFO] [ALSA]: Buffer size: 3072 frames [INFO] [ALSA]: Can pause: no.

Aha! Crossing the 54 ms threshold gives us an extra 1024 frames of buffer. That very well may explain the performance drop-off at 53 ms and below.

But hang on, can we go lower than that, performance be damned? It doesn’t appear we can. Even setting latency down to 2 ms gives the exact same output, and I have no idea if the driver is even honoring such a low latency setting (unlike tinyalsa, alsa says nothing about whether it accepts or rejects the latency setting in the log), and I don’t have the equipment to test audio latency to such a fine degree.

But now I’m intrigued. Despite the ostensibly lower latency that tinyalsa appears to accept, it seems to stick to a pre-defined buffer size which is actually BIGGER than what I can achieve with alsa, and raising or lowering the latency value seems to do close to nothing. The latency setting actually does appear to do something in alsa, however, and the smaller buffer size it achieves despite supposedly having a higher latency value makes me wonder if tinyalsa is not just a teensy little bit of a cheater, and alsa is actually the more performant of the two.

I have to admit I am a bit ignorant in regards to a lot of these things, so if someone who actually knows how these drivers work could correct me and tell me if my hunch is correct or not would be great, at least so I can put this to rest and finally leave well enough alone. :stuck_out_tongue: