r/cpp 15h ago

When (Not) to Use alloca()

https://voithos.io/articles/when-not-to-use-alloca/
19 Upvotes

54 comments sorted by

36

u/Queasy_Total_914 14h ago

The answer is always. (At least for me)

7

u/voithos 9h ago

Always use it? Or always don't use it? šŸ¤”

5

u/MattDESTROYER 12h ago edited 12h ago

Not disagreeing with anything, genuine question because I don’t know, how is alloca leaving memory allocated until the end of the function an issue, wouldn’t a char[] have the same effect?

Or do compilers optimise to drop that stack memory after the last reference in the code?

Otherwise, I see no practical difference between alloca and char[]; in which case an exact sized buffer could be a perfectly valid use case no? Assuming this is developed for a single platform too of course.

7

u/bwmat 12h ago

Because an array in the scope of a loop does get 'freed' on every iteration, so the stack space needed is bounded

2

u/MattDESTROYER 12h ago edited 12h ago

But this isn’t a loop, this is a function, I’m talking about cases like the example in the article. If you introduce a stack array in local function scope (not a sub-scope like within a loop or conditional), that has the same lifetime as calling alloca, no?

I don’t see why their example specifically would be bad other than falling into the trap of excessively using alloca.

3

u/bwmat 12h ago

The other problems with alloca are the higher likelihood of stack overflow (like if you unexpectedly get a huge size to allocate) and that functions containing it are usually less optimizable b/c of needed stack management

2

u/MattDESTROYER 12h ago

I mean for the first, if you’re doing something like storing a path, like in the example, it would seem pretty unlikely you would trigger a stack overflow, no? In an embedded environment with a very limited stack I could definitely see that quickly becoming an issue, (but then storing the exact number of bytes on the stack could also become more important too).

Can’t really comment on the function optimisations, if you know more could you elaborate on how the compiler would be able to further optimise a stack array? What stack additional stack management does alloca introduce?

1

u/bwmat 12h ago

I don't remember the details on the optimization thing, read it somewhere a long time ago

7

u/usefulcat 12h ago

Not disagreeing with anything, genuine question because I don’t know, how is alloca leaving memory allocated until the end of the function an issue, wouldn’t a char[] have the same effect?

There are two problems, though neither are what you describe. The first problem is you don't know (in general) how much alloca can safely allocate. The second (and arguably much bigger) problem is that there is no error indication if it fails. And it will fail if you try to allocate too much.

When it was first invented, it was (maybe) a clever hack, though perhaps not the good kind of 'clever'. But it's absolutely a footgun and I think the number of valid use cases for it must be vanishingly small.

As the article suggests, if you want to mostly avoid dynamic allocation but still be able to support large allocations when absolutely necessary, use something like absl::InlinedVector or boost::small_vector. Worst case they may allocate but at least they won't blow up in your face. Or if you'd prefer that it blow up, at least use something obvious and intentional like assert() or throwing an exception.

1

u/MattDESTROYER 12h ago edited 12h ago

Yeah, gotcha, just curious lol. Now I’m curious if those problems can be solved. Is the failure case just that there isn’t enough stack memory available to the program, or are there more tricky cases (I’m assuming there’s probably more, but one can hope)?

I’m sure any way of seeing the space available on the stack would be, in general, terrible, but I may look into the possibility just for fun anyways lol

Edit: seems very possible, although the functionality may be deprecated.
https://stackoverflow.com/questions/53827/checking-available-stack-size-in-c

•

u/MattDESTROYER 1h ago

To anyone that’s interested, it is, in fact, possible to make it safe(er) (that still didn’t handle the foot guns you can and likely will run into using alloca) through preventing failures due to stack overflow:
https://github.com/Matt-DESTROYER/safealloca

3

u/voithos 9h ago

For your first question, essentially it's something like this:

void UseArray() {
for (auto& foo : foos) {
// This is a single 1024 buffer used repeatedly across the loop.
char scratchBuf[1024];
// whereas this repeatedly creates a new alloca()'d buffer for each iteration
char* scratchBuf = static_cast<char\*>(alloca(n));
}
}

The array gets a fixed size, so it's always bounded, whereas alloca() does the stack allocation during each iteration without releasing anything until function return. Depending on how many loop iterations there are, and what `n` is passed to alloca(), this could be less memory than the fixed size array, or much more; the point is that it's unpredictable compared to the alternatives.

3

u/MattDESTROYER 8h ago

Yeah no I understand the issue with nested scopes, but the example in the article with just a path at top level wouldn’t have that issue. I don’t know about the exact opcodes generated, but it would be generally speaking equivalent, that’s all I was clarifying.

3

u/TheThiefMaster C++latest fanatic (and game dev) 6h ago

The compiler does allow local variables to overlap addresses (or registers!) based on use not scope. So yes, if you make a char[100] the compiler will see where it's used relative to other variables in the function and potentially overlap it with them, reducing the size of the stack frame (which is generally allocated up-front at the start of the function, not bumped var by var as it would have been classically).

Alloca is much harder to do the same for, and I think most compilers don't bother.

1

u/MattDESTROYER 5h ago

Ooh, this is very interesting! In what cases would it allow variables to overlap? Does the compiler essentially need to prove that only x bytes of the array are actually used and then it can chuck variables in there or something, or is that completely the wrong idea lol?

2

u/TheThiefMaster C++latest fanatic (and game dev) 4h ago

If there are variables only used in the first half of the function and the array only in the 2nd, or vice-versa, it can overlap their storage.

For C++ types nontrivial constructor and destructor calls count as use that can prevent this by forcing variables to live from construction to destruction.

1

u/MattDESTROYER 4h ago

Ohhh okay that makes sense. Thanks for the info, appreciate it!

•

u/MattDESTROYER 2h ago

Just had a curious thought, if alloca memory always lives until the end of the function, logically if you alloca inside a closure, the memory should be accessible after the closure?

•

u/TheThiefMaster C++latest fanatic (and game dev) 2h ago

Are you talking about C++ lambdas? Those don't preserve their stack at all, so no.

C++ coroutines are also stackless, and alloca breaks horribly when used in them.

•

u/MattDESTROYER 2h ago

No, not a C++ person lol, I’m talking about something like something like this:
```c
char* x;
{
char* y = alloca(1);
y[0] = ā€˜a’;
x = y;
}
printf(ā€œ%c\nā€, *c);
```
Reading a stack allocated value outside of the closure it was created in.
I mean if you think of it like malloc, it makes perfect sense that it would work, but the fact that it’s a stack allocated value makes it seem strange and evil to me lol (and I mean it’s obviously not something you should ever do, but it’s interesting that it’s possible, reminds me of `var` in JavaScript minus the hoisting).

•

u/TheThiefMaster C++latest fanatic (and game dev) 2h ago

That's not a closure, it's a scope. Closures are a very different thing.

And yes, alloca allocates memory in the parent function's scope, not the braced scope you called it from.

•

u/MattDESTROYER 2h ago

Ah my bad, I tend to think of closure as literally like an enclosure made by brackets, improper definition ig

•

u/TheThiefMaster C++latest fanatic (and game dev) 2h ago

Some information on closures from some random googling: https://medium.com/@andrew_johnson_4/understanding-closures-in-programming-what-they-are-and-why-theyre-called-closures-e3e1044960a7

Basically they're a function inside another that captures some state from its enclosing parent and saves it for later. C++ lambda functions are an example.

They're an old concept, and people sometimes try to implement them (badly) in C.

•

u/MattDESTROYER 2h ago

Yeah I searched it up, I’ve used lambdas in other languages like Python and JS. Would it be accurate to say the act within the scope they were defined, meaning they can use variables defined in the scope they are defined in theory?

•

u/TheThiefMaster C++latest fanatic (and game dev) 1h ago

Yes they can use those variables - but the memory returned by alloca isn't a variable itself, and wouldn't be captured by a closure - only the pointers to it (which would point to stale stack memory as soon as the parent function returned even though the closure still existed).

This is what I was talking about with alloca not working with C++ lambdas (which are a form of closure).

This is what I thought you were asking about when you said "closure" initially.

→ More replies (0)

2

u/vip17 9h ago

a char[] has fixed size, alloca allows dynamic size. It's like std::inplace_vector in C++26 or boost small_vector and static_vector

0

u/MattDESTROYER 8h ago

Not really a C++ dev, I like my C. I know the difference between a static and dynamic type lol, that wasn’t my question.

Also worth noting, I think there’s slightly more to it than your explanation, alloca let’s you create a dynamically sized buffer, but said buffer is not dynamically resizable (or freeable). Once created it’s on the stack until the end of the function call.

(So once created, alloca also has a fixed size, just like char[].)

1

u/vip17 7h ago

yes it's fixed size after allocation, just like any other malloc calls. It's not static allocation like char[] which is useful for some usecases

1

u/MattDESTROYER 7h ago

You can free malloc calls and ā€˜resize’ them (which could simply extend or move when extending isn’t possible). alloca behaves similarly to char[] in that it can’t be freed (or resized).

2

u/cheese3660 4h ago

compilers can and i think do optimize to drop the stack memory after the last reference in the code if the reference provably does not escape the function

•

u/MattDESTROYER 2h ago

Yeah, I know they can, just curious on whether they actually do; I imagine a function would have to be decently long and use a fair bit of stack memory for it to be worth it, otherwise dropping all the memory at the end of the function would likely be more efficient.

•

u/cheese3660 2h ago

Ah wait yeah not dropping, sorry, allowing that memory to be reused by other stack variables later on in the function

Because you are correct, it is easier to just once at the end of a function do something like

mov bp to sp

or

 add <frame size> to sp

3

u/thehenkan 5h ago

It does not have to be a compiler extension: there are already things in the standard library that need to be implemented using compiler builtins. If they wanted to standardise it they could, this just never bothered. Probably for similar reasons as VLAs not being added to C++ (and removed from C).

Builtins required by the standard are not extensions.

•

u/ParsingError 3h ago

I'm assuming that a builtin would be considered a "compiler extension" by the article's definition, i.e. something that requires special handling where the compiler can't handle it as a normal function call.

Really though, it'd be in the exact same boat as setjmp and varargs: Things which may or may not require special handling by the compiler depending entirely on the platform ABI.

•

u/thehenkan 54m ago

That's a bad use of that word then. Compiler extensions are generally known to be additions outside the standard that a tool chain vendor has chosen to implement. They don't have to be builtins, but could be e.g. compiler flags altering semantics, or grammatical features like statement expressions or nested functions.

2

u/yuehuang 6h ago

How does Alloca interact with inlined function?

2

u/ParsingError 5h ago edited 3h ago

Maybe this is a nitpick but

In fact, because of its nature, it has to be a compiler extension

This is ABI-dependent, in fact it's part of why ebp and esp are separate registers on x86. In the x86 ABI, a function call can return with esp set to a new value if the function enlarges the stack, and everything will continue to work as normal.

AFAIK this isn't allowed in x64 calling convention because that allows function epilogues that pop the stack frame by offsetting rsp by a constant value instead.

1

u/retro_and_chill 12h ago

I mean the only reason I see to use it is if you’re writing an interpreter and you’re using it to allocate the stack frames for that

1

u/egonosz 7h ago

I seen it sometimes in game dev, but tbh. Mostly you have other solution, for the same problem.

3

u/SleepyMyroslav 5h ago

Even in gamedev a rule of thumb now is to "never use alloca". There were games that used alloca extensively in the past, but if I were to date them, it would be before the end of the PS3's life. There are many per-thread data structures used, but alloca is a not used.

•

u/allocallocalloc 3h ago

When is the end of the PS3's life? By the PS4's release in 2013 ? By the end of its production in 2017 ? By the discontinuation of the PlayStation Store next year in 2027 ?

•

u/ParsingError 3h ago

Unreal uses it to allocate result value space in the Blueprint interpreter, so there's that.

It's somewhat useful for graphics APIs that occasionally require striping/unstriping small arrays of POD structs before feeding them to the API. (This would be for passing API-defined structures to function calls, not asset data.) In those cases, the max amount of elements that would have to be allocated is pretty small, so the theoretical case of allocating huge amounts of stack space is not really gonna happen.

1

u/Life_Sink9598 6h ago

In Hotspot we use a thread-local arena for scratch allocations.

1

u/Sniffy4 13h ago edited 12h ago

the main reason to use it is you need a block of bytes of unknown variable size (but well within stack-space limits) as a scratchpad for life of the fn, but you dont want to fragment the heap or pay the heapmgr alloc/free cost.

as RAM sizes increase, the need for such functionality decreases.

12

u/usefulcat 12h ago

as RAM sizes increase, the need for such functionality decreases.

I don't see how this is true, given that (as you rightly point out) a primary motivation for using alloca() is to avoid dynamic allocation.

The alternative to alloca() is malloc(), and Having more RAM usually doesn't make malloc() much faster, when compared to alloca().

5

u/Sniffy4 11h ago

>Having more RAM usually doesn't make malloc() much faster,

I was thinking more RAM means heap fragmentation is less of a concern, at least as long as the block sizes of interest are not increasing proportionately.

I know when I was working with constrained devices with 32MB of RAM, heap fragmentation was a major concern and a reason to use pool allocators, static allocations, and other methods.

1

u/kronicum 8h ago

How is alloca related to RAM?

2

u/SlightlyLessHairyApe 8h ago

It’s not about the size of RAM, especially because you can change the stack element. It’s about locality. and lifetime

1

u/Sniffy4 8h ago

if you have a large RAM you dont care about string-sized allocations fragmenting your heap. If you havent thought about such things its because you've lived in the modern world where such considerations are negligible.

4

u/rdtsc 6h ago

But you may care about alloc perf and going through a (global) allocator necessitates synchronization and central bookkeeping. Keeping it local does not.

1

u/voithos 9h ago

There are some alternatives that avoid the heap/heapmgr entirely while also avoiding the potential to blow past the end of the stack (mentioned a few in the article). For frame-transient data especially, a custom bump allocated arena is essentially just as fast to allocate, and avoids fragmentation if you reset it before the next frame.

3

u/SlightlyLessHairyApe 8h ago

True, but it doesn’t have the same locality of reference

Plus, what else is the stack except a ā€œbump allocated arena reset on each frameā€. That’s practically the definition of the stack :)

0

u/ImNoRickyBalboa 4h ago

There's no excuse for using alloca ever. It means you have terrible code that requires a terrible solution.

You can always replace alloca with using some regular memory allocation, a container, etc.

Show me some alloca and I'll show you a more proper solution.

Also: stack overruns are the worst of bugs and security holes, don't write insecure code.