函数包装器中的堆栈分配/函数中的分配

发布于 2024-12-23 01:50:37 字数 657 浏览 2 评论 0原文

我正在寻找一种将堆栈分配包装在抽象数据类型中的方法。例如,我想要一个可以通过堆栈上的分配严格工作的向量。当然,我最大的障碍是 alloca 只能在当前堆栈框架内工作 - 因此我没有看到将其包装到函数中的简单方法。

到目前为止,我认为做到这一点的唯一方法是使用类似宏的函数,这些函数保证被编译到给定的堆栈帧中。我不喜欢这种方法,因为它不像人们希望的那样类型友好,并且需要比预期更详细的命名。

无论如何,我可以得到一个函数来在其调用者堆栈上分配吗?我知道这通常会破坏立即调用堆栈,因此很可能该函数也必须以某种方式强制内联。我不清楚我有什么选择,所以我正在寻找一些想法,或指向可能的选择的指示。


注释

最终目标是类似 std::vector 的东西,它严格在直接函数堆栈上工作。显然它只会作为 const 对象传递给被调用者,并且它的生命随着函数的结束而结束。

C 方法很好,只要它比我的基于宏的方法更好。虽然一些支持宏也是可以接受的。

我知道这是一个相当具体的优化,最好我希望能够(使用标志)打开/关闭它(仅使用普通的 std::vector 进行调试)。它会给我们代码的重要部分带来轻微的速度提升,但可能不足以证明通过太多奇怪的结构使其不可读。

答案:很可能这是不可能的,只有宏观方法才有效。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

信仰 2024-12-30 01:50:37

你不能。
当函数返回时,其堆栈被展开,堆栈指针返回到之前的位置。如果你不想造成真正的混乱,就必须这么做。 alloca 所做的只是移动堆栈指针,因此函数返回会撤消此分配。
宏会起作用,因为它们只是将代码添加到同一函数中。但它会很丑陋,没有真正的改进希望。

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.

走走停停 2024-12-30 01:50:37

下面是一个用于分配栈上数组的示例宏,它通过辅助内联函数尽可能地利用 C++ 类型安全性和编译时检查:

#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

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
高跟鞋的旋律 2024-12-30 01:50:37

使用堆栈分配的主要好处可能是绕过标准库 malloc/new 分配器。从这个角度来看,使用堆栈并不是唯一的选择。

堆栈分配的一种替代方法是使用基于 mmap() 系统调用的自定义内存分配器。 mmap() 分配的内存可以用作自定义分配器的存储,而不是堆栈。为了避免经常调用 mmap(),由 mmap() 分配的内存区域应该被缓存,例如在全局线程特定变量中。

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.

メ斷腸人バ 2024-12-30 01:50:37

当然,我最大的障碍是 alloca 仅在当前堆栈框架内工作 - 因此我没有看到将其包装到函数中的简单方法。

好吧,如果您只需要分配一次(也就是说,如果您有一个始终足够的最大容量),您可以在默认参数中调用 alloca

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;
    }
};

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;
    }
};
不语却知心 2024-12-30 01:50:37

您始终可以实现自己的自定义分配器,该分配器与线程堆栈一样高效。根据我的经验,alloca 可能非常危险,如果隐藏在某些 C++ 类层次结构中(即在 for 循环中),很容易就会破坏堆栈。

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.

顾冷 2024-12-30 01:50:37

堆栈实际上不太适合容器类使用的分配类型。例如,当向量需要扩展其容量时,它很可能会分配一个新的存储区域,复制现有的项目(因此 C++ 对容器中使用的对象的复制和默认构造函数有要求) ,并释放原来的存储空间。不用说,这对基于堆栈的存储造成了严重破坏,在函数退出之前堆栈无法释放。 (vector 可以在不进行复制的情况下就地扩展capacity 的唯一方法是使用 realloc 函数,该函数在 C++ 中没有等效项,最重要的是你,没有 alloca 等价物。)

此外,alloca 只真正适用于 C++ 中的 POD 类型,而容器绝对不能。

编辑: 这个问题的答案部分解决了问题:它从堆栈中为向量分配初始存储,但如果需要更多容量,则从堆中分配。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文