Fixed-size array vs Dynamically allocated array for audio callback buffers.

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

Hey all, this is my first post here - KVR seems to be one of the most thriving communities for audio dev, I'm looking forward to joining in on the discussion :ud:

Anyway, to business:

I'm curious about the performance implications of using a fixed-size array for an audio callback buffer rather than a dynamic array (like the c++ `vector`). All audio frameworks that I've come across seem to use a dynamic array, which I can understand for programs who may need to change their buffer size during runtime, or allocate many buffers at once in the same process.

I'm currently working on a project where 1. I do not need to change the buffer-size at run-time and 2. There will be no more than two buffers allocated on my audio thread at any one time. For this reason I've been having a go at using a fixed size array instead, as I figure having the compiler prepare the allocation on a stack frame would be *much* faster than dynamic allocation (right?) . I'm using bindings to PortAudio (I'm developing my project in Rust) as my backend for driving the audio thread and it seems to be handling the fixed size array without problems. The audio thread seems to run at *least* as fast but I'm finding it hard to bench mark due to the current state of my project so I thought I'd ask:

What are your experiences with using fixed-size arrays for audio calback buffers? Do you have any examples of where they are used in the wild? Is it reasonable to expect a performance increase using a fixed-size array instead of a dynamic array/vector?

Also, would using a fixed-size array allow for much smaller buffer sizes (i.e. something like 32 samples instead of something like the stereotypical 256 samples) without any performance implications? Or are there other reasons larger buffer sizes are used other than to reduce the number of dynamic allocations?

Thanks :D

Post

Whether you allocate a buffer as fixed or dynamic size makes little difference, as long as you don't do such allocation while the audio is running: the act of allocating or deallocating a buffer in audio thread can cause a drop-out, but rest of the time it doesn't really matter. Some variants of dynamic arrays (including some implementations of std::vector) do bound checking for array indexes, which you probably don't want, so using a custom RAII array might be preferable over std::vector (and similarly in a language like Rust you might want to make your inner loops "unsafe" to get rid of such overhead; no personal experience with Rust, but I saw a presentation about it).

The main reason audio frameworks do dynamic allocation is just so they can deal with whatever blocksize is requested by the upstream components (eg. the host for a plugin, audio driver for an application, etc). On the other hand, if you want to split longer blocks into some internal maximum block size (or possibly process in completely fixed buffers) then you can certainly allocate fixed size buffers as well. In any case, performance it really doesn't make much difference.

Post

mystran wrote:Some variants of dynamic arrays (including some implementations of std::vector)
I doubt that is true for the operator [] in any sane modern implementation of std::vector when doing release builds. std::vector::at() is by standard required to do the bounds checking, though.

Post

Xenakios wrote:
mystran wrote:Some variants of dynamic arrays (including some implementations of std::vector)
I doubt that is true for the operator [] in any sane modern implementation of std::vector when doing release builds. std::vector::at() is by standard required to do the bounds checking, though.
Probably true, but I would make too many assumptions about sanity of STL implementations. Many of them do check for debug builds, which can be somewhat of a pain (if you are trying to debug something else) when debug builds have a tendency of running ridiculously slow even without such overhead.

Post

mystran wrote:debug builds
Real Men(tm) don't do debug builds! ;) (Actually I am half serious...Since the product that is going to be released to the public isn't going to be debug build anyway, I personally tend to be working with release builds most of the time when developing, so I will be working with realistic stability and performance scenarios...Of course sometimes I will spend some fun time with debug builds and the debugger.)

Post

If you need to declare a new vector (can't just use the host's) AND copy the audio buffer into it, then it's best to use a heap allocated vector for compatibility. Although I don't think I've seen a host which gives plugins blocks larger than 16384 per channel.

Post

To be honest, I might be over-thinking things and using std::vector might be just as well as long as you just make sure to initialize it to fixed size once when the blocksize changes (and then cache it between call-backs). I just tend to personally prefer custom classes that have exactly those methods that you are supposed to use and nothing else (eg. no copy constructors allowed, no implicit reallocation, and so on) just to keep the code as clear as possible. :)

Post

mystran wrote:have exactly those methods that you are supposed to use and nothing else (eg. no copy constructors allowed, no implicit reallocation, and so on) just to keep the code as clear as possible. :)
Sure, that's a reasonable approach in many cases!

Post

Xenakios wrote:
mystran wrote:Some variants of dynamic arrays (including some implementations of std::vector)
I doubt that is true for the operator [] in any sane modern implementation of std::vector when doing release builds. std::vector::at() is by standard required to do the bounds checking, though.
MSVC implementation does bound check at least in debug mode.

Reallocating a vector is not problematic if the size is more or less constant, as if you allocate a smaller chunk, the actual memory will not be reallocated (which is why sometimes to really clear a vector, you would swap your old vector with a new empty one).

Post

Miles1981 wrote:
Xenakios wrote:
mystran wrote:Some variants of dynamic arrays (including some implementations of std::vector)
I doubt that is true for the operator [] in any sane modern implementation of std::vector when doing release builds. std::vector::at() is by standard required to do the bounds checking, though.
MSVC implementation does bound check at least in debug mode.
So what if it does bounds checking in debug mode? Are you distributing debug builds that have to be run under the debugger to end users...? (I know in extreme cases debug builds run under the debugger so slow already you don't want any extra slowness, but then it is probably a good time to reflect on why are you developing running debug builds anyway...What exactly does the debugger help with in performance sensitive audio DSP code?)

Post Reply

Return to “DSP and Plugin Development”