C++ 固定大小阵列与alloca(或VLA)

C++ 固定大小阵列与alloca(或VLA),c++,c,performance,alloca,C++,C,Performance,Alloca,通过声明一个固定大小的数组,alloca()何时比在堆栈上分配内存更可取 详情: 正如我们所知,alloca()是一个有争议的函数。如果不小心使用,可能会导致堆栈溢出。如果使用得当,它可以避免堆分配,从而从紧循环中节省几纳秒。在《为什么alloca被认为是不好的》一书中,有几个顶级答案主张偶尔使用alloca 从堆栈进行分配的另一种方法是简单地声明一个固定大小的数组。此策略的示例可以在中的arena类中找到。(该代码当然是C++的,但是这个概念仍然适用于C)< /P> 使用alloca与固定大

通过声明一个固定大小的数组,
alloca()
何时比在堆栈上分配内存更可取


详情:

正如我们所知,
alloca()
是一个有争议的函数。如果不小心使用,可能会导致堆栈溢出。如果使用得当,它可以避免堆分配,从而从紧循环中节省几纳秒。在《为什么
alloca
被认为是不好的》一书中,有几个顶级答案主张偶尔使用
alloca

从堆栈进行分配的另一种方法是简单地声明一个固定大小的数组。此策略的示例可以在中的
arena
类中找到。(该代码当然是C++的,但是这个概念仍然适用于C)< /P> 使用
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);
}
#包括
#包括
#包括
#包括
#包括
无效foo_alloca(需要常数大小){
printf(“foo_alloca(%zu)\n),需要记忆);
char*mem;
bool used_malloc=false;
如果需要(记忆)
使用
alloca()
与使用固定大小的数组相比,有哪些利弊

  • 可移植性。
    alloca()
    不是标准的C库函数。固定大小的数组是该语言的一部分

  • 可分析性。定期分析代码内存使用情况的工具支持通过固定侧数组进行堆栈深度分析。
    alloc()
    可分析性可能/可能不存在

  • 空间效率。
    alloca()
    分配被禁止的内存空间。固定大小的数组往往会过度分配

  • 代码效率/速度当然是一个实现问题,需要分析来比较性能。预计不会有显著差异

  • VLA的优点/缺点与alloca()类似,只是它是C99标准的一部分,但在C11中是可选的


  • 这是
    C
    code.1)“代码> > MalCal不被调用——这对C++没有作用,2)在C++语言中没有VLA。如果函数递归调用,一个小的过度分配会很快变成一个巨大的过度分配。我大部分都是从这里的假设出发的,所以不要引用我。我想不出它会分配什么原因。g大于请求的确切数量。
    malloc
    必须考虑以一种能够有效释放重新分配内存的方式来管理内存。在堆栈上,它只需将堆栈指针向后移动任意远,就可以使用它。@Riley我怀疑
    alloca
    通常不需要进入内核模式。如果需要的话,它可能只需要扩展堆栈空间,而不是每次调用都需要扩展堆栈空间。不过,我不知道如何实际确定glibc函数是否进入内核模式。在使用
    strace
    编译并运行一个简单的测试后,
    alloca
    似乎不会进行系统调用。因此,它应该不会慢很多一个固定数组。
    alloca
    在内存不足时不会给出任何警告,只是UB。你能举一个分析堆栈深度的工具的例子吗?我不熟悉这种分析。谢谢。@praxelitic想到的第一个:分析堆栈/内存使用情况。通过不允许递归,绝对最大堆栈深度/内存使用是可计算的,对于在受限嵌入式内存环境中工作的编译器来说非常重要。