固定大小数组与 alloca(或 VLA)

Fixed size array vs alloca (or VLAs)

什么时候 alloca() 比通过声明固定大小的数组在堆栈上分配内存更可取?


详情:

众所周知,alloca()是一个有争议的函数。使用不当,会导致堆栈溢出。如果使用得当,它可以通过避免堆分配从紧密循环中缩短几纳秒。在 this question 关于为什么 alloca 被认为不好的问题中,几个热门答案提倡 偶尔 使用 alloca.

另一种从堆栈分配的方法是简单地声明一个固定大小的数组。在 Howard Hinnant's stack allocator 中的 arena class 中可以找到此策略的示例。 (该代码当然是 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] = '[=10=]';
    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] = '[=10=]';
    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。如果这种理解是错误的,请提出来。

What are the trade-offs of using alloca() vs a fixed size array?

  1. 便携性。 alloca() 不是标准的 C 库函数。固定大小的数组是语言的一部分。

  2. 可分析性。定期分析代码内存使用情况的工具通过固定端数组支持堆栈深度分析。 alloc() 可分析性 may/may 不存在。

  3. Space效率。 alloca() 分配被禁止的内存 space。固定大小的数组往往会过度分配。

  4. 代码 efficiency/speed 肯定是一个实施问题,需要进行分析以比较性能。预计不会有显着差异。

  5. VLA pros/cons 类似于 alloca() 除了它是 C99 标准的一部分,但在 C11 中只是可选的。