何时alloca()
优于通过声明固定大小的数组在堆栈上分配的内存?
细节:
我们知道,alloca()
是一个有争议的函数。如果不小心使用,可能会导致堆栈溢出。如果使用得当,它可以避免堆分配,从而缩短紧循环的几纳秒。在这个关于为什么alloca
被认为是坏的问题上,几个最前面的答案提倡偶尔使用alloca
。
另一种从堆栈中分配空间的方法是简单地声明一个固定大小的数组。这种策略的一个例子可以在Howard Hinnant的堆栈分配器的arena
类中找到。(这段代码当然是c++,但概念仍然适用于C)
使用alloca
与固定大小的数组的权衡是什么?什么时候,如果有的话,一个明显优于另一个?它只是一个性能问题,应该在每个单独的情况下进行经验测试(当性能是一个关键目标并且已经确定了热点时)?固定大小的数组更悲观——它总是按照我们的意愿在堆栈上分配多少就分配多少——但不清楚这是好是坏。
只是为了尽可能清楚,这里有一个非常简单的例子,两个函数实现似乎有理由使用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] = ' ';
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] = ' ';
puts(mem);
if (used_malloc)
free(mem);
}
int main()
{
foo_alloca(30);
foo_fixed(30);
foo_alloca(120);
foo_fixed(120);
}
另一个与alloca
非常相似的选项是VLAs。据我所知,从alloca
和VLAs获得的内存基本上具有相同的行为,所以这个问题也适用于VLAs。如果这种理解是错误的,就说出来。
使用
alloca()
与固定大小的数组的权衡是什么?
-
可移植性。
alloca()
不是标准的C库函数。固定大小数组是语言的一部分。 -
可分析性。定期分析代码内存使用情况的工具支持通过固定侧阵列进行堆栈深度分析。
alloc()
可分析性可能存在,也可能不存在。 -
空间效率。
alloca()
分配被禁止的内存空间。固定大小的数组往往会过度分配。 -
代码效率/速度当然是一个实现问题,需要分析来比较性能。
-
VLA的优点/缺点类似于
alloca()
,除了它是C99标准的一部分,但仅在C11中可选。