堆上分配的数据是否比栈上分配的数据访问速度更慢?

5
我听到了对此事有不同的看法。
一些人认为,访问在堆上分配的数据(例如std::vector或通过malloc分配的动态数组)总是比访问在栈上分配的数据稍微慢一些,因为进程必须始终通过中间指针访问该数据,这个指针可能位于完全不同的内存区域;另一方面,他们认为,访问在栈上分配的数据不需要经过这个中间指针,而且这些数据很可能已经被缓存。
其他人声称,只有在堆上分配数据时比在栈上慢(由于mallocmmap和类似函数的开销),但访问不会慢,除非栈和堆位于不同的物理驱动器上。
真相到底是什么?

1
没有绝对的真理。它取决于您整个系统——代码和硬件。在 NUMA 机器上甚至更加复杂。如果确实需要知道,可以对应用程序进行分析和测试。 - Andrew Henle
3
抱歉,栈和堆都与物理驱动器没有关联。 - Mark Setchell
当内存访问进入“交换空间”(通常在物理驱动器上)时,您的代码不再是“快速”的,无论是堆栈还是堆。 - 2785528
“我听到了关于这个问题的不同意见。这两种说法怎么可能都是真的呢?” - 2785528
局部性原理对于现代处理器而言至关重要,L1缓存中的数据访问速度很快,其他情况都会变得非常糟糕。栈通常具有优势,因为由于栈指针,它始终处于活动状态;编译器自然地分组可能的访问,并且由于其较小,很难出现错误。但是,由于它太小了,程序员在解决小任务时不必过多关注性能问题。所以作为“总是这样做”指导并不是很有用。这只是一个陈词滥调,与“穿越马路前应左右看”的意义不大。 - Hans Passant
@HansPassant,为什么不把这个评论转换成答案? - user4385532
2个回答

3
真相是(至少在现代大多数CPU上)堆栈和堆执行效果相同,因为它们都只是RAM的一部分。因此解除引用指针基本相同。
不同之处在于,堆栈被预先分配给进程/线程,因此您不需要使用malloc和free系统调用来处理它。特别是malloc是昂贵的。另一个区别是可能会有一些特定的CPU指令用于处理堆栈,从而增加性能(例如汇编语言的push,pop)。但是这些指令很可能与内存访问(例如将内存加载到寄存器中)无关。
另一个区别是,如果堆栈耗尽,则程序很可能会崩溃。而如果堆耗尽,则操作系统可能会为您使用交换,从而将性能降低数千倍。
缓存未命中当然是一个因素,堆比堆栈更容易发生。但这只是因为堆比堆栈大得多。但请注意,缓存未命中并不重要,除非您正在编写极其cpu密集的代码。
现在你是正确的,std :: vector必须进行额外的解除引用时间。但这里慢的是解除引用,而不是那些指针在堆栈或堆上。它们在哪里并不重要。双重解除引用始终比单个解除引用慢。
现在也有可能堆栈和堆位于不同的物理设备上。这两个设备可能具有不同的速度(堆可能更快)。但这再次与堆栈和堆本身无关。这可能发生在任何两个内存部分之间。您真的无法对此做任何事情。甚至操作系统也不能(好吧,也许它可以,我不确定)。这是主板的事情。此外,很可能主板会降低较快设备的时钟速度。

0
有人说,访问堆分配的数据(例如std::vector或通过malloc分配的动态数组)比访问栈分配的数据总是略慢,因为进程必须始终通过中间指针访问那些数据,这些数据可能位于完全不同的内存区域;另一方面,他们认为,访问在栈上分配的数据不需要经过这个中间指针,并且这些数据很可能已被缓存。

这是牛粪。只有静态的可以无需中间指针寄存器就能访问。堆栈数据通过一个寄存器(SP),一些处理器具有补充堆栈寄存器(例如BP,AP)来访问。

此外,内存就是内存就是内存。唯一使内存成为堆栈的是按堆栈方式访问它。

有人声称,与在堆上分配相比,在堆栈上分配仅仅会更慢(由于malloc、mmap和类似函数的开销),但是访问不会。
这是正确的。在堆栈上分配数据只需要一条指令。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接