FloatVectorOperations::add

I have trouble (confused) trying to add an array of floats into another array using FloatVectorOperations::add.

Destination array: float destArray[8][16]. To illustrate I’ll call these elements ‘a’ and ‘b’.
Source array: float sourceArray[8][16][20]. To illustrate I’ll call these elements ‘x’, ‘y’, and ‘z’.

What I need to do is add a certain amount, which is going to be my FloatVectorOperations::add numValues parameter, of the sourceArray floats in ‘z’, in a specific ‘x’ and ‘y’, and sum it into destArray in a specific ‘a’ and ‘b’.

Note that when I do this ‘a’ and ‘x’ is actually the same, plus ‘b’ and ‘y’ is the same.

So naively I had hoped I could do this;
a = x = 3;
b = y = 7;
numValues = 10;

FloatVectorOperations::add (destArray[ a ][ b ], sourceArray[ x ][ y ], numValues);

Besides it won’t take those arguments, I needed it to sum the first 10 floats, out of 20, in sourceArray[3][7] into destArray[3][7].

Is there any way to get this to work by for example reorganizing one or both arrays?

Don’t use C-style multidimensional arrays. Use a C++ container, like juce AudioBuffer or std::vector/array.

pretty sure they just did it to illustrate a point here.

about the point: you just need to make sure that your inputs work with the arguments of the static functions. add can add a single value to a dest buffer or it can add a source buffer to a dest buffer. so say we’re talking about some numeric type T here, first arg is T* and 2nd one is either T or T*

I welcome any opportunity to learn more about C++ and appreciate any help, but I was wondering if you could elaborate your answer a bit?

Do you mean the C-style multidimensional arrays in this case does not work, and/or are just bad programming (and if so please explain), or?

I read several places on the Web, that if you know the static size of an array, meaning it would never change dynamically, there would be no reason not to use ‘old’ C-style multidimensional arrays, and as you are off course well aware I am doing DSP were every CPU tick counts. I might be wrong, but I would assume ‘old’ C-style multidimensional arrays are faster than AudioBuffer, and even a tiny bit faster than std::vector/array.

C style arrays are generally much less safe than C++ data structures. You’re much more likely to make programming errors. And they are not faster.

If you have an array that you know will always be a constant size, use std::array. Otherwise, use std::vector. If you’re dealing with multiple channels of audio samples, juce::AudioBuffer really is the best container. Your code will be plenty fast, and it will also be much more likely to actually work reliably.

Out of curiosity, and since my monster synth like speed, I did a crude multi-dimensional data test which show std::vector is about 31% slower, taking more time, than C style, and for me surprisingly AudioBuffer takes virtually the same time as C style!

In the test code below, which I put into the RenderNextBlock area of SynthVoice.h, CPU ticks for each of the three methods are summed into variables, and every 1000th run the average time is calculated and put into another variable. Variables are displayed in my PluginEditor. Compiler was in Release mode.

// Array Time Test
{ // C style
	startTimerTicks = Time::getHighResolutionTicks ();
	float arrayTest[2][1024];

	for (int x = 0; x < 2; x++)
	{
		for (int y = 0; y < 1024; y++)
		{
			arrayTest[x][y] = 2.0f / (y + 1) - 1.0f;
		}
	}

	endTimerTicks = Time::getHighResolutionTicks ();
	testValue[0] += endTimerTicks - startTimerTicks;

}

{ // Vector
	startTimerTicks = Time::getHighResolutionTicks ();
	std::vector<std::vector<float>> arrayTest (2, std::vector<float> (1024));

	for (int x = 0; x < 2; x++)
	{
		for (int y = 0; y < 1024; y++)
		{
			arrayTest[x][y] = 2.0f / (y + 1) - 1.0f;
		}
	}

	endTimerTicks = Time::getHighResolutionTicks ();
	testValue[1] += endTimerTicks - startTimerTicks;
}

{ // AudioBuffer

	startTimerTicks = Time::getHighResolutionTicks ();
	AudioBuffer<float> arrayTest;
	arrayTest.setSize (2, 1024, false, false, false);

	for (int x = 0; x < 2; x++)
	{
		for (int y = 0; y < 1024; y++)
		{
			arrayTest.setSample (x, y, 2.0f / (y + 1) - 1.0f);
		}
	}

	endTimerTicks = Time::getHighResolutionTicks ();
	testValue[2] += endTimerTicks - startTimerTicks;
}

timerCurrentRun++;
timerCounter++;

if (timerCurrentRun >= runsPerTest)
{
	// C style
	timerAverageTime = float (testValue[0]) / runsPerTest;
	testValue[0] = 0;
	testValue[5] = timerAverageTime;

	// Vector
	timerAverageTime = float (testValue[1]) / runsPerTest;
	testValue[1] = 0;
	testValue[6] = timerAverageTime;

	// AudioBuffer
	timerAverageTime = float (testValue[2]) / runsPerTest;
	testValue[2] = 0;
	testValue[7] = timerAverageTime;

	timerCurrentRun = 0;
}			

Before I’d argue over speed, I would look at getting the stuff working. Ideally in a readable/maintainable manner, which is my concern with the multi dimensional arrays.

When you tried:

You supplied a float& instead of a float* ,which is what the FloatVectorOperations::add expected.

1 Like

While accessing a std::vector should not make any speed difference, creating one, especially a two dimensional one like that has a lot more overhead as it involves a few heap allocations, while the array is completely on the stack, so “creating” it is as fast as incrementing your stack pointer by the array size.

Furthermore, if your compiler is clever enough it can figure out that the computation result assigned to the array is never used and might optimize away the whole write access or even the variable. So your benchmark might generate unrealistic results.

Re-run your tests again, but start the timer after creating the array/vector and I bet that you get nearly the same results for all cases. In real audio code, you’d allocate your storage once in prepare (where speed doesn’t matter that much) and access it in process (where speed matters).

In fact there is another reason why using heap data structures is better in that case, which is the risk of stack overflows when creating too much of those arrays. That can obviously not happen with heap-allocated memory.

1 Like

Thank you very much for the test suggestions, which I just did. However vector is still “much” slower, although now down from being 31% slower to 27% slower.

Thank you for pointing out the actual reason for my post :wink:

To clarify I just need to sum the numVales in ‘z’ into another variable. What I do not need to is to add ‘b’ and ‘z’ values and put into ‘b’. I don’t know if that makes sense, but I need something like this;

float sum = 0.0f;
FloatVectorOperations::add (sum, sourceArray[ x ][ y ], numValues);

It just happens to be that I store the sum in the ‘b’ of destArray[ a ][ b ].

Why are you working with multidimensional arrays in the first place? I think there’s probably a simpler solution to whatever it is you’re trying to achieve.

If you want to sum together all the elements of an array, that’s not what FloatVectorOperations::add does. This function adds a value to every element of an array.

Because my synth has 8 tone generators, each with up to 16 unison voices, and up to 20 times oversampling, and I need to gather all of these samples, before down sampling, and moving on to next stage of my synth.

I suspected that, thanks for pointing that out. I’ll look for a totally different solution then other than to manually add in a loop.

So I would instead do

struct ToneGenerator
{
   struct Voice
   {
   };

   std::array<Voice, 16> voices;
};

std::array<ToneGenerator, 8> tones;

Even if you can get the code with multidimensional arrays working, it is much more complex than it needs to be IMHO.

My synth works great with my few C style multi-dimensional arays, thanks. Actually you may have seen one my Youtube demo’s in some earlier posts where you participated.

ok if we could stop talking about multidimensional arrays’ pros and cons for a while and focus on this comment, that would be great. because now i’m curious and i want to know what exactly does vector do that is supposed to be any slower than AudioBuffer when allocation is not a thing in the measurements anymore? can anyone explain that or guess what is going on? i would have guessed AudioBuffer to be the slowest option tbh, because it has that clear-atomic in it and sometimes also does some other special stuff to ensure ease of use, while c-style arrays, std::array and std::vector all just give direct access to the things they hold or point to

Now it is quite possible that my test results is not consistent in any type of situation. I suggest you make a simple test yourself in an area of your code where such data is used quite frequently.

Yes I was very surprised AudioBuffer was as fast as C-style multi-dimensional arrays. However that only happened when I compiled for release. When I compiled in Debug mode it was clearly the slowest. So perhaps when compiling in Release mode, some of the checks are bypassed? Just a thought.

yeah i’m not so surprised about AudioBuffer being almost as fast as c-style. i mean it has some ifs and an atomic in it, but they are not used often and don’t make too much of a difference usually. but i’m very surprised about the alleged drop in performance by vector. because vector really doesn’t do anything crazy as far as i know. it’s like std::array, but on the heap instead of stack. or what else is there to it? i’m just saying because while i myself have no idea why you measured these values it does seem to me like there could still be a mistake in your assumptions and maybe someone who reads this and knows more wants to point out what it could be. or a confirmation why vector indeed has to be slower

Like I said please try and do a quick simple test and see if you come up with similar, or not. I am quite curious :slight_smile: