Когда alloca()
предпочтительнее, чем память, выделенная в стеке путем объявления массива фиксированного размера?
Подробности:
Как мы знаем, alloca()
является спорной функцией. Неосторожное использование может привести к переполнению стека. При разумном использовании, он может сократить несколько наносекунд из узкой петли, избегая выделения кучи. В этот вопрос о том, почему alloca
считается плохим, некоторые из лучших ответов выступают за случайный использование alloca
,
Другой способ выделения из стека — просто объявить массив фиксированного размера. Пример этой стратегии можно найти в arena
класс в Распределитель стека Говарда Хиннанта. (Этот код, конечно, C ++, но концепция все еще применима к C.)
Каковы компромиссы использования alloca
vs массив с фиксированным размером? Когда, если вообще, одно явно предпочтительнее другого? Является ли это просто вопросом производительности, который должен быть проверен эмпирически в каждой отдельной ситуации (когда производительность является ключевой целью, и горячая точка уже определена)? Массив фиксированного размера более пессимистичен — он всегда выделяет столько, сколько мы хотим выделить в стеке, — но не ясно, хорошо это или плохо.
Просто чтобы быть как можно более ясным, вот очень простой пример двух реализаций функций, где кажется целесообразным использовать либо alloca
или массив фиксированного размера:
#include <alloca.h>
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
void foo_alloca(const size_t mem_needed) {
printf("foo_alloca(%zu)\n", mem_needed);
char* mem;
bool used_malloc = false;
if (mem_needed <= 100)
mem = alloca(mem_needed);
else {
mem = malloc(mem_needed);
used_malloc = true;
}
assert(mem_needed != 0);
// imagine we do something interesting with mem here
mem[0] = 'a';
mem[1] = 'b';
mem[2] = 'c';
mem[3] = '\0';
puts(mem);
if (used_malloc)
free(mem);
}
void foo_fixed(const size_t mem_needed) {
printf("foo_fixed(%zu)\n", mem_needed);
char* mem;
char stack_mem[100];
bool used_malloc = false;
if (mem_needed <= 100)
mem = stack_mem;
else {
mem = malloc(mem_needed);
used_malloc = true;
}
assert(mem_needed != 0);
// imagine we do something interesting with mem here
mem[0] = 'a';
mem[1] = 'b';
mem[2] = 'c';
mem[3] = '\0';
puts(mem);
if (used_malloc)
free(mem);
}
int main()
{
foo_alloca(30);
foo_fixed(30);
foo_alloca(120);
foo_fixed(120);
}
Еще один вариант очень похож на alloca
это VLA. Насколько я знаю, память получена от alloca
и VLA ведут себя по существу одинаково, поэтому этот вопрос относится и к VLA. Если это понимание неверно, просто упомяните об этом.
Каковы компромиссы использования
alloca()
vs массив с фиксированным размером?
Переносимость. alloca()
не является стандартной библиотечной функцией C. Массивы фиксированного размера являются частью языка.
Analyzability. Инструменты, которые анализируют использование памяти кода, регулярно поддерживают анализ глубины стека с помощью фиксированных боковых массивов. alloc()
анализируемость может / не может существовать.
Пространственная эффективность. alloca()
выделяет запрещенную область памяти. Массивы фиксированного размера имеют тенденцию перераспределять.
Эффективность / скорость кода, безусловно, является проблемой реализации, и для сравнения производительности потребуется профилирование. Существенной разницы не ожидается.
VLA плюсы / минусы это как alloca()
за исключением того, что это является частью стандарта C99, но только факультативно в C11.
Других решений пока нет …