0

在使用标准向量保存我的国际象棋引擎的移动列表后,我意识到因为国际象棋的平均因子为 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 向量相比),还是只会增加堆栈溢出的机会?我的 Move 结构需要大约 28 字节的内存,我怀疑引擎会递归搜索(使用 alpha-beta)到最多 7 或 8 层,因此可能会从堆栈中使用一些 28 * 35 * 8 ~ 8kb max . 我在某处读到,通常堆栈的限制为 1Mb,所以这应该不会太多吧?

编辑:感谢下面的答案,我现在意识到我最初对 alloca() 所做的理解是错误的。但是,我仍然想知道是否可以通过以下方式使用 alloca():

    int main()
    {
        int* arr = (int) alloca(sizeof(int));
        arr = alloca(sizeof(int) * 2 ));//is this 'resizing' valid?
    }
4

2 回答 2

3

该函数alloca在堆栈上分配内存,一旦alloca被调用的函数返回,内存就不再可用。这意味着一旦MoveList构造函数或resize函数返回,内存就不再可用。您认为您将能够在MoveList对象的生命周期内以某种方式使用此内存的假设是错误的。

对您来说最好的选择是使用std::vector和保留。

于 2014-06-06T12:44:54.520 回答
1

您似乎不了解非标准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,它从不重新分配元素(以稍微慢一点的访问为代价)。

于 2014-06-06T12:53:12.570 回答