这样使用alloca()是否有效?

Is this use of alloca() valid?

本文关键字:是否 有效 alloca      更新时间:2023-10-16

在使用标准向量保存我的国际象棋引擎的移动列表后,我意识到,由于国际象棋的平均因子为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,它从不重新分配元素(代价是访问速度略慢)。