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

时间:2011-12-27 14:53:30

标签: c++ gcc alloca

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

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

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


注意:

最终目标类似于std::vector,它严格地用于直接函数堆栈。显然它只会作为const对象传递给callees,它的生命以函数结束。

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

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

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

6 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

使用堆栈分配的主要好处可能是标准库malloc / new allocator的旁路。在这种情况下,使用堆栈不是唯一的选择。

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

答案 2 :(得分:1)

  

我当然最大的障碍是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;
    }
};

答案 3 :(得分:1)

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

答案 4 :(得分:1)

这是一个用于分配堆栈数组的示例宏,它通过辅助内联函数尽可能地利用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

答案 5 :(得分:0)

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

此外,alloca仅适用于C ++中的POD类型,这些容器绝对不是。

编辑: The answer to this question部分解决了问题:它从堆栈中为vector分配初始存储空间,但是如果需要进一步的容量,则从堆。