这是两个重叠的问题——我希望尝试将 alloca() 用于大型数组,而不是在堆上分配动态大小的数组。这样我就可以提高性能而不必进行堆分配。但是,我得到的印象堆栈大小通常很小?增加堆栈大小以便我可以充分利用 alloca() 是否有任何缺点?是不是我拥有的 RAM 越多,我可以按比例增加我的堆栈大小就越大?
EDIT1:最好是Linux
EDIT2:我没有指定的大小 - 我宁愿知道如何判断决定限制/边界的因素。
这是两个重叠的问题——我希望尝试将 alloca() 用于大型数组,而不是在堆上分配动态大小的数组。这样我就可以提高性能而不必进行堆分配。但是,我得到的印象堆栈大小通常很小?增加堆栈大小以便我可以充分利用 alloca() 是否有任何缺点?是不是我拥有的 RAM 越多,我可以按比例增加我的堆栈大小就越大?
EDIT1:最好是Linux
EDIT2:我没有指定的大小 - 我宁愿知道如何判断决定限制/边界的因素。
在大多数 unix-y 平台上,堆栈大小(默认情况下)为 8MB,在 Windows 上为 1MB(即,因为 Windows 具有从堆栈外问题中恢复的确定性方法,而 unix-y 平台通常会抛出通用SIGSEGV
信号)。
如果您的分配很大,那么在堆上分配与在堆栈上分配之间不会有太大的性能差异。当然,堆栈每次分配的效率稍高一些,但是如果您的分配很大,则分配的数量可能会很小。
如果你想要一个更大的类似堆栈的结构,你总是可以编写自己的分配器,它从 malloc 获得一个大块,然后以类似堆栈的方式处理分配/解除分配。
#include <stdexcept>
#include <cstddef>
class StackLikeAllocator
{
std::size_t usedSize;
std::size_t maximumSize;
void *memory;
public:
StackLikeAllocator(std::size_t backingSize)
{
memory = new char[backingSize];
usedSize = 0;
maximumSize = backingSize;
}
~StackLikeAllocator()
{
delete[] memory;
}
void * Allocate(std::size_t desiredSize)
{
// You would have to make sure alignment was correct for your
// platform (Exercise to the reader)
std::size_t newUsedSize = usedSize + desiredSize;
if (newUsedSize > maximumSize)
{
throw std::bad_alloc("Exceeded maximum size for this allocator.");
}
void* result = static_cast<void*>(static_cast<char*>(memory) + usedSize);
usedSize = newUsedSize;
return result;
}
// If you need to support deallocation then modifying this shouldn't be
// too difficult
}
程序主线程获取的默认堆栈大小是特定于编译器(和/或特定于操作系统)的东西,您应该查看相应的文档以了解如何扩大堆栈。
您可能无法将程序的默认堆栈扩大到任意大的大小。
然而,正如已经指出的那样,您可能能够在运行时创建一个线程,并使用您想要的大小的堆栈。
无论如何,alloca()
一次分配的大缓冲区并没有太大的好处。您不需要多次释放和重新分配它。
alloca()
和new
/之间最重要的区别malloc()
是,当您从当前函数返回时,分配的所有内存alloca()
都将消失。
alloca()
仅对小型临时数据结构有用。
它仅对小数据结构有用,因为大数据结构会破坏堆栈的缓存局部性,这会给您带来相当大的性能损失。数组作为局部变量也是如此。
alloca()
仅在非常特殊的情况下使用。如果不确定,请不要使用它。
一般规则是:不要将大数据结构(> = 1k)放在堆栈上。堆栈不缩放。这是一种非常有限的资源。
回答第一个问题:堆栈大小通常相对于堆大小较小(这在大多数 Linux 应用程序中都是如此)。
如果您计划的分配相对于实际的默认堆栈大小来说很大,那么我认为最好使用堆中的动态分配(而不是尝试增加堆栈大小)。使用内存(填充、读取、操作)的成本可能会远远超过分配的成本。在这种情况下,通过从堆栈分配,您不太可能看到可衡量的好处。