这个问题可能听起来相当简单,但这是我与另一位开发人员的争论。

我小心地在我可以的地方进行堆栈分配,而不是堆分配。他和我说话,看着我的肩膀,并评论说,这是没有必要的,因为他们的表现是一样的。

在我的印象中,增加堆栈是一个常数时间,而堆分配的性能取决于当前堆的复杂性,包括分配(找到合适大小的孔)和反分配(缩小孔以减少碎片,如果我没有弄错的话,许多标准库实现在删除过程中需要时间来做这件事)。

在我看来,这可能非常依赖于编译器。在这个项目中,我特别使用了一个用于PPC架构的Metrowerks编译器。对这种组合的深入了解将是最有帮助的,但一般来说,对于GCC和msvc++,情况如何?堆分配不如堆栈分配高效吗?没有区别吗?还是差异如此之小以至于变成了毫无意义的微观优化。


当前回答

自然,堆栈分配更快。使用堆分配,分配器必须在某处找到空闲内存。使用堆栈分配,编译器只需要给你的函数一个更大的堆栈框架就可以完成,这意味着分配完全不需要花费时间。(我假设您没有使用alloca或任何东西来动态分配堆栈空间,但即使这样,它也非常快。)

但是,您必须警惕隐藏的动态分配。例如:

void some_func()
{
    std::vector<int> my_vector(0x1000);
    // Do stuff with the vector...
}

您可能认为这会在堆栈上分配4 KiB,但您错了。它在堆栈上分配vector实例,但该vector实例又在堆上分配它的4 KiB,因为vector总是在堆上分配它的内部数组(至少除非您指定了一个自定义分配器,这里我不会深入讨论)。如果您希望使用类似stl的容器在堆栈上进行分配,则可能需要std::array或boost::static_vector(由外部boost库提供)。

其他回答

堆栈要快得多。它在大多数架构上只使用一条指令,在大多数情况下,例如在x86上:

sub esp, 0x10

(将堆栈指针向下移动0x10个字节,从而“分配”这些字节供变量使用。)

当然,堆栈的大小是非常非常有限的,因为你很快就会发现你是否过度使用堆栈分配或尝试进行递归:-)

同样,没有什么理由去优化那些不需要它的代码的性能,比如通过分析来证明。“过早的优化”通常会导致比它本身价值更多的问题。

我的经验法则:如果我知道我将在编译时需要一些数据,并且它的大小在几百字节以下,我就会对它进行堆栈分配。否则我进行堆分配。

这不仅仅是堆栈分配更快。您还可以在使用堆栈变量方面获得很多好处。它们有更好的参考位置。最后,折价也便宜得多。

一般来说,正如上面几乎每个答案所提到的,堆栈分配比堆分配快。堆栈的push或pop是O(1),而从堆中分配或释放可能需要遍历之前的分配。但是,您通常不应该在紧凑的性能密集型循环中进行分配,因此选择通常取决于其他因素。

做出这样的区分可能会有好处:您可以在堆上使用“堆栈分配器”。严格地说,我认为堆栈分配是指分配的实际方法,而不是分配的位置。如果你在实际的程序堆栈上分配了很多东西,这可能会因为各种各样的原因而变得很糟糕。另一方面,在可能的情况下使用堆栈方法在堆上进行分配是分配方法的最佳选择。

既然你提到了《Metrowerks》和《PPC》,我猜你指的是Wii。在这种情况下,内存是非常宝贵的,在任何可能的情况下使用堆栈分配方法都可以保证您不会在片段上浪费内存。当然,这样做需要比“普通”堆分配方法更加小心。对每种情况进行权衡是明智的。

前面提到过,堆栈分配只是移动堆栈指针,即大多数架构上的一条指令。将其与堆分配中通常发生的情况进行比较。

操作系统以链表的形式维护部分空闲内存,有效负载数据由指向空闲部分起始地址的指针和空闲部分的大小组成。为了分配X字节的内存,将遍历链表,并按顺序访问每个音符,检查其大小是否至少为X。当找到大小为P >= X的部分时,将P分成大小为X和P-X的两个部分。更新链表并返回指向第一部分的指针。

如您所见,堆分配取决于许多因素,如您请求的内存大小、内存的碎片程度等等。

堆栈分配要快得多,因为它所做的只是移动堆栈指针。 使用内存池,您可以从堆分配中获得类似的性能,但这会略微增加复杂性,并带来令人头痛的问题。

此外,堆栈与堆不仅是性能方面的考虑;它还告诉您许多关于对象的预期生存期的信息。