5

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

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

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


笔记:

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

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

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

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

4

6 回答 6

2

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

于 2011-12-27T15:09:07.547 回答
1

使用堆栈分配的主要好处可能是绕过标准库 malloc/new 分配器。有鉴于此,使用堆栈并不是唯一的选择。

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

于 2011-12-27T15:23:05.737 回答
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;
    }
};
于 2011-12-27T16:38:16.223 回答
1

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

于 2011-12-27T23:52:01.070 回答
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
于 2017-10-04T11:49:16.390 回答
0

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

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

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

于 2011-12-27T15:09:27.453 回答