Допустимо ли это использование alloca()?

После использования вектора std для хранения моего списка ходов в шахматном движке я понял, что, поскольку у шахмат есть коэффициент усреднения 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;
    }

В частности, если, скажем, емкость 10 недостаточна для alloca() в первый раз, синтаксически допустимо (и правильно) просто снова вызывать alloca(), чтобы выделить больше памяти? Даст ли этот метод лучшую производительность (по сравнению со стандартным вектором с помощью Reserve()) или только увеличит вероятность переполнения стека? Моей структуре Move требуется около 28 байт памяти, и я подозреваю, что движок будет рекурсивно искать (используя альфа-бета) максимум до 7 или 8 слоев, так что, возможно, из стека будет использоваться максимум 28 * 35 * 8 ~ 8 КБ., Я где-то читал, что обычно стеки имеют ограничение в 1 Мб, так что это не должно быть слишком много, верно?

Изменить: Благодаря ответам ниже, я теперь понимаю, что мое первоначальное понимание того, что делал alloca(), было неверным. Однако я все еще хотел бы знать, возможно ли использовать alloca() следующим образом:

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

2 ответа

Решение

Функция 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, который никогда не перераспределяет элементы (за счет немного более медленного доступа).

Другие вопросы по тегам