在使用标准向量保存我的国际象棋引擎的移动列表后,我意识到,由于国际象棋的平均因子为35(即从一个典型位置开始的35个合法移动),向量会调整很多,从而对移动生成器的性能产生负面影响。解决这个问题的一种方法(我今天才意识到)是为向量保留最小容量。然而,使用alloca()的可能性引起了我的注意。这可能是一个非常简单的问题,但是关于alloca()的文档非常少,关于如何使用它的示例非常非常少。
因此,来自可变大小类的分配的答案提到堆栈分配不能调整大小。然而,以下内容是否有效?
struct MoveList
{
MoveList(): capacity(10)
{
moves = (Move*) alloca(sizeof(Move) * 10);
}
void resize()
{
capacity *= 2;
moves = (Move*) alloca(sizeof(Move) * capacity );
}
void push_back();
Move* moves;
int size;
int capacity;
}
具体来说,如果第一次调用alloca()的容量为10是不够的,那么再次调用alloca()来分配更多的内存在语法上是否有效(并且正确)?这个方法会提供更好的性能(与reserve()的std vector相比),还是只会增加堆栈溢出的机会? My Move结构体需要大约28字节的内存,我怀疑引擎将递归搜索(使用alpha-beta)到最大7或8层,因此可能会从堆栈中使用大约28 * 35 * 8 ~ 8kb的最大值。我在哪里读到过,堆栈通常有1Mb的限制所以这应该不会太多,对吧?
编辑:多亏了下面的答案,我现在意识到我最初对alloca()所做的理解是错误的。但是,我仍然想知道是否有可能以以下方式使用alloca(): int main()
{
int* arr = (int) alloca(sizeof(int));
arr = alloca(sizeof(int) * 2 ));//is this 'resizing' valid?
}
函数alloca
在堆栈上分配内存,一旦调用alloca
的函数返回,内存就不再可用。这意味着只要MoveList
构造函数或resize
函数返回,内存就不再可用。你认为在MoveList
对象的生命周期内可以使用这些内存的假设是错误的。
您最好的选择是使用std::vector
并保留。
您似乎不理解非标准alloca()
表达式实际做什么。它在调用函数的堆栈帧中分配内存。在您的例子中,这意味着分配的空间(在本例中分配给moves
成员)的生命周期是构造函数的生命周期:
MoveList(): capacity(10)
{
moves = (Move*) alloca(sizeof(Move) * 10);
... moves is valid from this point
// "moves" stops being valid at this point
}
因为构造函数的其余部分是空的,所以这是而不是您想要的。(此外,alloca()
还有一个副作用,即防止调用函数内联——这是另一个意想不到的副作用。)换句话说,要回答标题中的问题,alloca()
的这种使用是无效的。
即使它在某种程度上是有效的,因为alloca()
没有对应的调整或释放分配的内存(也不能有,由于它的工作方式),它非常不适合任何需要调整区域大小的情况-这正是你试图使用它的方式。
std::vector
的大小调整能力通常已经考虑到指数增长的因素,所以添加自己的是不必要的。如果你不确定,衡量绩效,看看什么适合你。也许对于您的情况,调用std::vector<T>::reserve()
以确保向量以乐观大小开始就足够了,从而消除了重新分配的需要。或者使用std::deque
,它从不重新分配元素(代价是访问速度略慢)。