【问题标题】:Favorability of alloca for array allocation vs simple [] array declarationalloca 对数组分配与简单 [] 数组声明的优势
【发布时间】:2014-03-02 03:32:50
【问题描述】:

阅读一些 Apple 代码,我偶然发现了以下 C 块

alloca(sizeof(CMTimeRange) * 3)

这和通过分配堆栈内存是一样的吗

CMTimeRange *p = CMTimeRange[3]?

对性能有什么影响吗?需要释放内存吗?

【问题讨论】:

  • 我想你的意思是CMTimeRange someVariableName[3]
  • 您能否提供更多关于该调用的代码以获取一些上下文?

标签: objective-c c macos memory-management


【解决方案1】:

假设 Joachim 指出您的意思是 CMTimeRange someVariableName[3]...

两者都会在堆栈上分配内存。

我猜alloca() 将不得不在你的函数序言之后添加额外的代码来进行分配......函数序言是编译器自动生成的代码,用于在堆栈上创建空间。结果是,您的函数在编译后可能会稍微大一些,但不会大很多……一些额外的指令来修改堆栈指针和可能的堆栈帧。我猜如果它不在条件分支中,编译器可以优化调用,或者只是将它提升到条件分支之外?

我在没有优化的情况下在我的 MQX 编译器上进行了实验……它不是 Objective-c,只是 C,也是一个不同的平台,但希望这是一个足够好的近似值,并且确实显示了发出的代码的差异。我在堆栈上使用了两个带有大数组的简单函数,以确保必须使用堆栈空间(变量不能仅存在于寄存器中)。

显然不建议将大数组放在堆栈上...这仅用于演示目的。

unsigned int TEST1(unsigned int stuff)
{
    unsigned int a1[100]; // Make sure it must go on stack
    unsigned int a2[100]; // Make sure it must go on stack
    a1[0] = 0xdead;
    a2[0] = stuff + 10;
    return a2[0];
}

unsigned int TEST2(unsigned int stuff)
{
    unsigned int a1[100]; // Make sure it must go on stack
    unsigned int *a2 = alloca(sizeof(unsigned int)*100);
    a1[0] = 0xdead;
    a2[0] = stuff + 10;
    return a2[0];
}

生成了以下汇编程序:

测试1: 两个数组a1a2 都在函数序言中入栈...

   0: 1cfcb6c8                 push %fp
   4: 230a3700                 mov  %fp,%sp
   8: 24993901                 sub3 %sp,%sp,100    # Both arrays put on stack
   c: 7108                     mov_s    %r1,%r0
   e: 1b38bf98 0000dead        st   0xdead,[%fp,0xffff_fce0]        ; 0xdead
  16: e00a                     add_s    %r0,%r0,10
  18: 1b9cb018                 st   %r0,[%fp,0xffff_fe70]
  1c: 240a36c0                 mov  %sp,%fp
  20: 1404341b                 pop  %fp
  24: 7ee0                     j_s  [%blink]

测试2: 在序言中只有数组a1被放入堆栈...必须生成额外的代码行来处理alloca

   0: 1cfcb6c8                 push %fp
   4: 230a3700                 mov  %fp,%sp
   8: 24593c9c                 sub3 %sp,%sp,50 # Only one array put on stack
   c: 240a07c0                 mov  %r4,%blink
  10: 220a0000                 mov  %r2,%r0
  14: 218a0406                 mov  %r1,0x190       # Extra for alloca()
  18: 2402305c                 sub  %sp,%sp,%r1     # Extra for alloca()
  1c: 08020000r                bl   _stkchk         # Extra for alloca()
  20: 738b                     mov_s    %r3,%sp # Extra, r3 to access write via pointer
  22: 1b9cbf98 0000dead        st   0xdead,[%fp,0xffff_fe70]        ; 0xdead
  2a: 22400280                 add  %r0,%r2,10
  2e: a300                     st_s %r0,[%r3] # r3 to access write via pointer
  30: 270a3100                 mov  %blink,%r4
  34: 240a36c0                 mov  %sp,%fp
  38: 1404341b                 pop  %fp
  3c: 7ee0                     j_s  [%blink]

另外,alloca() 内存将通过指针访问(除非对此有聪明的编译器优化...我不知道),因此会导致实际的内存访问。自动变量可能会被优化为只是寄存器访问,这样会更好...编译器可以使用寄存器着色来确定哪些自动变量最好留在寄存器中,以及它们是否需要在堆栈上。

我快速搜索了 C99 标准(C11 大约是……我的参考资料有点过时了)。看不到对alloca 的引用,因此可能不是标准定义的函数。可能的劣势?

【讨论】:

  • 为什么alloca()动态数组版本相比需要额外的代码?
  • 如果你的函数序言将所有自动变量的 SP 减少了 5,然后,假设编译器没有优化,alloca() 必须生成另一个 SP -=尺寸。这是一点点额外的代码,但 OP 要求有所不同。
  • 不确定我是否关注。大概编译器必须更改堆栈帧以适应动态数组,所以我认为代码非常相似。
  • 嗯...实际上我很愚蠢并假设两者...但是假设至少有一个 other 自动变量,我认为这一点仍然成立。假设我有 4 个字节的自动变量。 Prologue 对帧中的其他位和鲍勃执行SP -= (4 + x)x。然后说alloca(4 bytes),必须生成另一个语句来执行 SP -= 4。现在,编译器可能会看到alloca(...) 调用并只发出一个SP -= 8,但又可能不会。再次,我意识到这是一个微不足道的差异......这有意义吗?
【解决方案2】:

如果你真的只想在堆栈上分配 3 的元素,那么使用 alloca 完全没有意义。仅当您在运行时具有取决于某些动态参数的可变长度,或者您在同一函数中执行未知数量的此类分配时,才有意义。

alloca 不是标准函数,因平台而异。 C 标准更倾向于引入 VLA,可变长度数组作为替代。

【讨论】:

  • 您好 Jens,请注意:C11 已将 VLA 降低为可选。仅在 C99 中需要
  • @Jimbo,唯一没有实现 VLA 的大玩家是 MS,而且看不到他们的 C11 编译器。所有其他人的编译器中都有它,他们不会删除该功能。
  • 好的,谢谢。上次我提到 VLA 时已经向我指出了这一点。看起来务实的答案是当时支持 VLA,尽管将来可能会下降,但我想如果供应商已经按照您所说的那样实施了 VLA,那是未知或不太可能
【解决方案3】:

这和通过...分配堆栈内存是一样的吗

我认为不完全是。声明局部变量会导致在进入堆栈帧时保留内存(通过从堆栈指针中减去变量的大小并调整对齐方式)。

看起来alloca(3) 通过在遇到堆栈指针时调整堆栈指针来工作。请注意手册页的“Bugs”部分。

alloca() 依赖于机器和编译器; 不鼓励使用它。

alloca() 有点不安全,因为它不能确保返回的指针指向一个有效且可用的内存块。所做的分配可能会超出堆栈的界限,甚至会进一步进入内存中的其他对象,而 alloca() 无法确定这样的错误。避免使用大量无限制分配的 alloca()。

在我看来,这两点加起来如下:

不要使用 ALLOCA

【讨论】:

    猜你喜欢
    • 2023-02-09
    • 2022-01-02
    • 2010-12-07
    • 1970-01-01
    • 1970-01-01
    • 2015-03-01
    • 2011-04-23
    • 2013-03-22
    • 2017-02-07
    相关资源
    最近更新 更多