Stack variables = unexpected memory overhead

While tuning the dynamic memory consumption of a large sketch, I noticed some behaviour that I would not expect. This has explained why my sketch is using far more RAM than my code flows would suggest.

I'm looking for confirmation if this is a known limitation of the AVR compiler as I cannot see it mentioned anywhere. I don't think its obvious, especially if you're an existing C/C++ programmer. I'm using Arduino IDE 0022.

So, I'm particularly focussing on memory use from local variables declared on the stack, e.g. "char buf[100]". Take this example function:

void fn (bool condition)
{
if (condition)
{
char buf[100] = "boo"; // Reserve a 100 character array
Serial.println (buf); // Just here to stop compiler optimising buf out completely, which it will otherwise
}
}

Looks innocent enough. Imagine we call it with "fn(false)". Obviously the "if" block is not entered but you would also expect the buf array is not allocated, right? Wrong. It does.

Likewise if you have a class declaration in that conditional-if, while it does not obviously call the class constructor, it DOES reserve space for the class members.

It seems the AVR compiler is not very clever in allocating memory for autos inside conditional blocks. My findings are showing that even if there are conditional sections in your functions, all memory for all autos will be allocated on entry to your function.

You can work around it by invoking another function and putting the local variables there or by using malloc/free, but this seems like a bug to me that many people will run into.

You can prove it yourself in various ways. I'm hoping this is a bug that's known or been fixed?

Chris

I'm hoping this is a bug that's known or been fixed?

I don't think it is a bug at all. The compiler has no way of knowing, at COMPILE time, how the function will be called AT RUN TIME. So, it needs to generate code that will encompass all possible ways of invoking the function.

That is, it can't make decisions on whether any condition will evaluate AT RUN TIME to true or false. It needs to generate code for each possibility.

You seem to have some unrealistic expectations of the compiler.

The AVR compiler is very limited in what it can provide because some of the devices it is working with have very small footprints to work with (and none of them provide an abundance of flash or ram to work with). The Stack Handler and Memory Manager are kept as simple as possible to keep them from hogging up program space, so don't expect them to be as full featured as their PC counterparts. This is simply the nature of dealing with small footprint embedded devices. It isn't a bug, but a limitation of the product.

It's not that unrealistic at all Paul, an auto variable can be allocated at the point of declaration, even if it is conditional, but it is compiler dependent. Some do allocate everything on function entry, some do not. For an embedded controller I would have hoped it did the more leaner allocation, which would have been more appropriate.

So, it seems its the way it is and I will code around it.

Chris

So what happens if you put this same code in a loop?

void fn (bool condition)
{
int x=0;
for(x=0; x<1000; x++) {
if (condition)
{
char buf[100] = "boo"; // Reserve a 100 character array
Serial.println (buf); // Just here to stop compiler optimising buf out completely, which it will otherwise
}
}
}

Wouldn't the complier have to include code to allocate buf on each iteration of the for() loop?

Leaner in what respect? A more functional memory allocator requires more code, which takes up more flash. And I assume there's a case where the buf would get allocated anyways, so the ram has to be available regardless. It certainly isn't clear to me that would have been more appropriate.

And I assume there's a case where the buf would get allocated anyways, so the ram has to be available regardless.

This. Worse, if the compiler did optimize the size of the stack frame for you, it would introduce some hard to trace bugs when you were short of stack space - sometimes it would work, sometimes crash, depending on the inputs and commensurate stack frame. Not a pretty picture.

http://www.nongnu.org/avr-libc/user-manual/group__alloca.html

Thanks, alloca works well for many of my use cases and is sometimes more convenient than malloc/free. I have a few places where I have class instances being instantiated conditionally but I just moved them into new functions to remove the constant overhead.

Chris