【发布时间】:2017-02-11 21:15:19
【问题描述】:
什么时候alloca() 比通过声明一个固定大小的数组在堆栈上分配的内存更可取?
详情:
众所周知,alloca() 是一个有争议的函数。乱用,会导致栈溢出。明智地使用,它可以通过避免堆分配从紧密循环中减少几纳秒。在 this question 关于为什么 alloca 被认为不好时,一些最重要的答案主张偶尔使用 alloca。
另一种从堆栈分配的方法是简单地声明一个固定大小的数组。这种策略的一个例子可以在Howard Hinnant's stack allocator 的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] = '\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。如果这种理解是错误的,请提及它。
【问题讨论】:
-
这是
C代码。 1)malloc调用未强制转换——这不适用于 C++,并且 2) C++ 语言中没有 VLA。 -
如果函数被递归调用,一个小的过度分配很快就会变成一个巨大的过度分配。
-
我在这里主要是出于假设,所以不要引用我的话。我想不出任何理由,它会分配比请求的确切数量更多的东西。
malloc必须考虑以一种可以有效释放重新分配内存的方式管理内存。在堆栈上,它可以将堆栈指针移回任何需要的位置,并完成它。 -
@Riley 我怀疑
alloca通常不需要进入内核模式。如果是这样,它可能只需要扩展堆栈空间,这不会在每次调用时发生。我不知道如何实际确定 glibc 函数是否进入内核模式。 -
用
strace编译并运行一个简单的测试后,alloca似乎没有进行系统调用。因此,它不应该比固定数组慢很多。alloca内存不足时不会发出任何警告,它只是 UB see here
标签: c++ c performance alloca