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

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

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

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


当前回答

通常,堆栈分配只是由堆栈指针寄存器中的减法组成。这比搜索堆快多了。

Sometimes stack allocation requires adding a page(s) of virtual memory. Adding a new page of zeroed memory doesn't require reading a page from disk, so usually this is still going to be tons faster than searching a heap (especially if part of the heap was paged out too). In a rare situation, and you could construct such an example, enough space just happens to be available in part of the heap which is already in RAM, but allocating a new page for the stack has to wait for some other page to get written out to disk. In that rare situation, the heap is faster.

其他回答

关于Xbox 360 Xenon处理器上的堆栈与堆分配,我了解到一件有趣的事情,这可能也适用于其他多核系统,即在堆上分配会导致进入临界区以停止所有其他核,这样分配就不会发生冲突。因此,在一个紧密循环中,堆栈分配是固定大小数组的方法,因为它可以防止停顿。

如果您正在为多核/多进程编码,这可能是另一个需要考虑的加速,因为您的堆栈分配将只由运行您的作用域函数的核心可见,而不会影响任何其他内核/ cpu。

堆栈分配是一对指令,而我所知道的最快的rtos堆分配器(TLSF)平均使用150条指令。此外,堆栈分配不需要锁,因为它们使用线程本地存储,这是另一个巨大的性能优势。因此,堆栈分配可以快2-3个数量级,这取决于您的多线程环境有多严重。

通常,如果关心性能,堆分配是最后的选择。一个可行的中间选项可以是一个固定池分配器,它也只有几个指令,每次分配开销很小,所以它非常适合固定大小的小对象。缺点是它只适用于固定大小的对象,本质上不是线程安全的,并且有块碎片问题。

通常,堆栈分配只是由堆栈指针寄存器中的减法组成。这比搜索堆快多了。

Sometimes stack allocation requires adding a page(s) of virtual memory. Adding a new page of zeroed memory doesn't require reading a page from disk, so usually this is still going to be tons faster than searching a heap (especially if part of the heap was paged out too). In a rare situation, and you could construct such an example, enough space just happens to be available in part of the heap which is already in RAM, but allocating a new page for the stack has to wait for some other page to get written out to disk. In that rare situation, the heap is faster.

我认为生命期很重要,被分配的东西是否必须以复杂的方式构造。例如,在事务驱动的建模中,您通常必须填写并将带有一堆字段的事务结构传递给操作函数。以OSCI SystemC TLM-2.0标准为例。

在栈上靠近操作调用的地方分配这些资源往往会导致巨大的开销,因为这种构造非常昂贵。好的方法是在堆上分配和重用事务对象,或者通过池或简单的策略,如“这个模块只需要一个事务对象”。

这比在每个操作调用上分配对象快很多倍。

原因很简单,该对象具有昂贵的结构和相当长的使用寿命。

我会说:两种都试试,看看哪种最适合你,因为这真的取决于你代码的行为。

关于这种优化有一个普遍的观点。

您得到的优化与程序计数器实际在该代码中的时间成正比。

如果您对程序计数器进行采样,您将发现它在哪里花费时间,这通常是在代码的一小部分,并且通常是在您无法控制的库例程中。

只有当你发现在对象的堆分配上花费了大量时间时,才会明显地更快地进行堆栈分配。