5

I'm looking for a way to wrap stack allocations in abstract data types. For example, I'd like to have a vector which can work strictly via allocations on the stack. My biggest hurdle of course is that alloca works only within the current stack frame -- thus I don't see an easy way to wrap this into a function.

So far the only way I see to do this is by using macro-like functions which are guaranteed to be compiled into a given stack frame. I don't like this approach since it isn't as type friendly as one would hope, and requires more verbose naming than desired.

Is there anyway I can get a function to allocate on its caller stack? I understand this would normally destroy the immediately calling stack, thus likely the function would also have to be forced inline somehow. I'm not clear on what options I have, so I'm looking for some ideas, or pointers towards possible options.


Notes:

The ultimate goal is something like a std::vector which works strictly on the immediate functions stack. Obviously it would only be passed as a const object to callees, and its life ends with the function.

C approach is fine so long as it is better than my macro based approach. Though some support macros are also acceptable.

I understand this is a fairly specific optimization, and optimally I'd like to be able to (with a flag) turn it on/off (using just an normal std::vector for debugging). It would give a minor speed boost to significant parts of our code, but probably not enough to justify making it unreadable via too many odd constructs.

Answer: Is most likely that it isn't possible and that only the macro approach would work.

edA-qa mort-ora-y
  • 30,295
  • 39
  • 137
  • 267
  • In short, you can't. `alloca` doesn't play well with the C++ object model. You can always use your own allocator for a standard container if you want tighter control over the memory allocations. – Kerrek SB Dec 27 '11 at 14:56
  • possible duplicate of [Looking for C++ STL-like vector class but using stack storage](http://stackoverflow.com/questions/354442/looking-for-c-stl-like-vector-class-but-using-stack-storage) – interjay Dec 27 '11 at 15:06
  • Also: http://stackoverflow.com/questions/4082532/is-there-an-allocator-that-uses-alloca-and-is-otherwise-c-stl-compliant – interjay Dec 27 '11 at 15:06
  • First link is a static size on the stack, that I know how to do, I'm wishing for a dynamic solution (I recognize it may be impossible). To second question, I don't need STL compliance, but the first answer there is likely the same here (simply it isn't possible). – edA-qa mort-ora-y Dec 27 '11 at 15:11
  • @DeadMG, why remove the C tag? I indicate I'm okay with a C approach -- especially since a C solution is more likely than a direct C++ one. – edA-qa mort-ora-y Dec 27 '11 at 15:18
  • Because this is a non-issue for C. This whole question exists because you're using C++ classes and objects. – Mike DeSimone Dec 27 '11 at 15:24
  • @MikeDeSimone, how is this a non-issue for C? If you know how to use functions in C to achieve this then please tell me, I'll accept that answer? – edA-qa mort-ora-y Dec 27 '11 at 15:28
  • Because C has no language-level notion of abstract data types! It gives you `alloca` and expects you to behave with it, and nothing more. (Recent flavors don't even give you `alloca` but expect you to use stack-based arrays with variable dimensions.) So even if you solve this with a C solution (which would ignore namespaces), it's still a C++ (read: first-world) problem. – Mike DeSimone Dec 27 '11 at 15:39

6 Answers6

2

You can't.
When a function returns, its stack is unwound, and the stack pointer goes back where it was before. It has to, if you don't want a real mess. All alloca does is move the stack pointer, so a function return undoes this allocation.
Macros would work, because they just add code to the same function. But it would be ugly, with no real hope of improvement.

ugoren
  • 16,023
  • 3
  • 35
  • 65
  • If C++ wanted to, it could track what objects were stored on the stack, so it could destroy them properly much like it does when unwinding during exception handling. Of course, some new kind of syntax would be needed; `alloca` wouldn't work out-of-the-box for the same reasons `malloc` doesn't work out-of-the-box when classes are involved. – Mike DeSimone Dec 27 '11 at 15:15
  • Perhaps I'm just looking for type-safe namespace aware macros then. :) – edA-qa mort-ora-y Dec 27 '11 at 15:16
  • If C++ wanted, it could turn the stack into a heap. But then, what advantage would it have over the heap? – ugoren Dec 27 '11 at 18:56
1

Here's an example macro for allocating an on-stack array, which takes advantage of C++ type-safety and compile-time checking as much as possible via a helper inline function:

#include <type_traits>
#include <alloca.h>

namespace Utils {

// A wrapper for alloca which allocates an array of default-constructible, trivially-destructible objects on the stack
#define ALLOC_ON_STACK_ARRAY(T, nMembers) \
        ::Utils::InitArrayOfTriviallyDestructibles<T>(alloca(sizeof(T) * nMembers), size_t(nMembers))

// Helper function for ALLOC_ON_STACK_ARRAY() defined above. Initialize a block of memory as an array.
template <typename T>
inline T* InitArrayOfTriviallyDestructibles(void* p, size_t nMembers)
{
        static_assert(std::is_trivially_destructible<T>::value, "The type is not trivially destructible");
        return new (p) T[nMembers] {};
}

} // namespace Utils
Isac Casapu
  • 1,163
  • 13
  • 21
1

The main benefit of using stack allocation is probably the by-pass of the standard library malloc/new allocator. In this light, using the stack is not the only option.

One alternative to stack allocation is using a custom memory allocator based on mmap() system call. The memory allocated by mmap() can be used as storage for the custom allocator instead of the stack. To avoid calling mmap() often the memory region allocated by mmap() should be cached, in a global thread-specific variable, for example.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
1

My biggest hurdle of course is that alloca works only within the current stack frame -- thus I don't see an easy way to wrap this into a function.

Well, if you only need to allocate once (that is, if you have a maximum capacity that will always be sufficient), you can call alloca inside a default argument:

template<typename T, size_t Capacity>
class stack_vector
{
    T* start_;
    size_t size_;

public:

    explicit stack_vector(void* memory = alloca(sizeof(T) * Capacity))
    {
        start_ = static_cast<T*>(memory);
        size_ = 0;
    }
};
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • Is the evaluation of default arguments guaranteed to happen within the caller's stack frame? At least in gcc that is. – edA-qa mort-ora-y Dec 27 '11 at 16:50
  • 3
    That doesn't make too much sense. If `Capacity` is indeed known at compile time, you could add a `char data[Capacity]` member. However, if you need to specify it at run-time, `explicit stack_vector(size_t capacity, void* memory = alloca(sizeof(T) * capacity))` doesn't work (default argument references parameter). – marton78 Jul 20 '12 at 12:31
1

You can always implement your own custom allocator that will be as efficient as thread stack. From my experience alloca can be very dangerous, and if hidden in some C++ class hierarchies (ie. in for loop) can easily blow your stack.

marcinj
  • 48,511
  • 9
  • 79
  • 100
0

The stack is really not a good fit for the kind of allocations that a container class uses. For example, when a vector needs to expand its capacity, it most likely allocates a new region of storage, copies the existing items over (hence the C++ requirements on copy and default constructors for objects used in containers), and releases the original storage. Needless to say, this plays havoc with stack-based storage, which cannot be released until the function exits. (The only way vector could expand capacity in-place without copying is using the realloc function, which has no C++ equivalent, and most importantly to you, no alloca equivalent.)

Further, alloca only really works with POD-types in C++, which containers most definitely are not.

EDIT: The answer to this question partly solves the problem: It allocates the initial storage for the vector from the stack, but if further capacity is needed, then it allocates from the heap.

Community
  • 1
  • 1
Mike DeSimone
  • 41,631
  • 10
  • 72
  • 96
  • `alloca` works fine with placement new on complex object types. The container would also be custom written to support this allocator, I'm not looking for a drop-in allocator for STL or anything. – edA-qa mort-ora-y Dec 27 '11 at 15:15
  • Yes, that other question is a very good optimization for the same type of thing I'm trying to optimize. In fact that type of vector is my next step. I was just hoping I could go a step further than that. – edA-qa mort-ora-y Dec 27 '11 at 15:21
  • 1
    The only reason it works with placement new is because you've computed the final object's actual storage size for it. Placement new is really just a way to call the constructor. However, you still have to call `alloca` from the function; you can't wrap it into another function. Even if you could force `inline` to always inline, there's no guarantee that one `inline` function's "stack" won't be reused by a later `inline` function's "stack". – Mike DeSimone Dec 27 '11 at 15:23