Is this use of alloca() valid?

820 Views Asked by At

After using an std vector to hold my move list for my chess engine, I realised that because chess has an averaging factor of 35 (i.e. something like 35 legal moves from a typical position), the vector was resizing a lot, thereby negatively impacting the performance of the move generator. One way around this (which I only realised today) is by reserving a minimum capacity for the vector. However, the possibility of using alloca() attracted my attention. This is probably a very simple question, but the documentation on alloca() is pretty scarce, with very very few examples of how to use it.

So an answer from Allocation of variable-sized class mentions that stack allocations cannot be resized. Nevertheless, would the following be valid?

    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;
    }

Specifically, if say, a capacity of 10 is insufficient from alloca() the first time around, is it syntactically valid (and correct) to simply call alloca() again to allocate some more memory? Will this method give better performance (compared to std vector with reserve()), or only increase the chance of a stack overflow? My Move struct requires some 28 bytes of memory, and I suspect that the engine will recursively search (using alpha-beta) to maybe 7 or 8 ply maximum so that perhaps some 28 * 35 * 8 ~ 8kb max will be used from the stack. I read somewhere that typically stacks have a limit of 1Mb so this should not be too much right?

Edit: Thanks to the answers below, I realise now that my initial understanding of what alloca() did was wrong. However, I would still like to know if it is possible to use alloca() in the manner below:

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

There are 2 best solutions below

5
On BEST ANSWER

The function alloca allocates memory on the stack, and the memory is no longer available as soon as the function in which alloca was called returns. It means that as soon as MoveList constructor or resize function returns, the memory is no longer available. Your assumption that somehow you will be able to use this memory during lifetime of MoveList object is wrong.

The best option for you is to use std::vector and reserve.

8
On

You don't seem to understand what the non-standard alloca() expression actually does. It allocates memory in the stack frame of the calling function. In your case, it means that the lifetime of allocated space (in this case assigned to the moves member) is that of the constructor:

   MoveList(): capacity(10)
   {
      moves = (Move*) alloca(sizeof(Move) * 10);
      ... moves is valid from this point

      // "moves" stops being valid at this point
   }

Since the rest of your constructor is empty, this is not what you intended. (Also, alloca() has a side effect of preventing the calling function to be inlined - another unintended side effect.) In other words, to answer the question from the title, this use of alloca() is not valid.

Even if it were somehow made valid, since alloca() has no counterpart to resize or free the allocated memory (nor can it have one, due to how it works), it is highly inappropriate for any situation where the area needs to be resized — which is exactly how you are trying to use it.

The resizing capacity of an std::vector normally already factors in exponential growth, so adding your own is unnecessary. If you are unsure, measure performance and see what works for you. Maybe for your case it would be sufficient to call std::vector<T>::reserve() to make sure that the vector starts off with an optimistic size, removing the need for reallocations. Or use an std::deque, which never reallocates elements (at the costs of slightly slower access).