是否更快地访问堆中的数据?

con*_*ist 38 c c++ heap performance stack

我知道这听起来像是一个普遍的问题而且我已经看过很多类似的问题(无论是在这里还是在网上),但它们都不是真的像我的困境.

说我有这个代码:

void GetSomeData(char* buffer)
{
    // put some data in buffer
}

int main()
{
     char buffer[1024];
     while(1)
     {
          GetSomeData(buffer);
          // do something with the data
     }
     return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果我在全局声明缓冲区[1024],我会获得任何性能吗?

我通过time命令在unix上运行了一些测试,执行时间之间几乎没有差异.

但我真的不相信......

理论上,这种变化应该有所作为吗?

Ton*_*roy 40

是否更快地访问堆中的数据?

并非......在我曾经工作过的每个架构上,所有进程"内存"都可以按照相同的速度运行,具体取决于CPU缓存/ RAM /交换文件保持当前数据的级别,以及任何硬件级同步延迟该内存上的操作可能会触发使其对其他进程可见,并包含其他进程/ CPU(核心)的更改等.

操作系统(负责页面错误/交换)以及硬件(CPU)捕获对已换出或未访问页面的访问,甚至不会跟踪哪些页面是"堆栈"与"堆". ..内存页面是一个内存页面.也就是说,全局数据的虚拟地址可以在编译时计算和硬编码,基于堆栈的数据的地址通常是堆栈指针相对的,而堆上的内存几乎总是必须使用指针访问,这可能是在某些系统上稍微慢一些 - 它取决于CPU寻址模式和周期,但它几乎总是微不足道 - 除非你写的是万亿分之一秒非常重要的东西,否则它甚至不值得一看或二流.

无论如何,在你的例子中,你将全局变量与函数本地(堆栈/自动)变量进行对比......没有涉及堆.堆内存来自newmalloc/ realloc.对于堆内存,值得注意的性能问题是应用程序本身正在跟踪在哪些地址使用了多少内存 - 所有需要一些时间来更新的记录作为指向内存的指针由new/ malloc/ 分发realloc,并且更多时间更新,因为指针是deleted或freed.

对于全局变量,内存的分配可以在编译时有效地完成,而对于基于堆栈的变量,通常有一个堆栈指针,每次使用编译时计算的局部变量(和一些内务数据)的大小的总和.调用一个函数.因此,当main()调用时可能需要一些时间来修改堆栈指针,但它可能只是被修改了一个不同的数量而不是在没有修改的情况下buffer修改,如果没有则修改,因此运行时性能没有任何区别.

  • 关于你的第一句话:我开始写同样的事情,但正如你在下文中指出的那样,它_不是_真的;事实是(在当今的大多数处理器上)速度并不取决于内存本身所在的位置,而是取决于之前访问的内容。 (3认同)
  • @dragonxlwang:大约是它的大小,是的。干杯。 (2认同)
  • 这是一个非常出色和彻底的答案。太感谢了。它确实消除了我对为什么堆栈和堆具有不同性能特征的许多困惑,尽管它们都分配在 RAM 中。特别是,堆栈指针可以在编译时计算出来,这一事实是一个巨大的洞察力! (2认同)

hac*_*cks 28

引自Jeff Hill的回答:

堆栈更快,因为访问模式使得从中分配和释放内存变得微不足道(指针/整数简单地递增或递减),而堆在分配或免费中涉及更复杂的簿记.此外,堆栈中的每个字节都经常被频繁地重用,这意味着它往往被映射到处理器的缓存,使其非常快.堆的另一个性能损失是堆(主要是全局资源)通常必须是多线程安全的,即每个分配和释放需要 - 通常 - 与程序中的"所有"其他堆访问同步.

在此输入图像描述

  • "访问堆中的数据的速度要快于堆栈中的数据吗?" 问题是,你的重点实际上是错误的,如果你拥有相同访问模式的相同数据,那么理论上堆应该和堆栈一样快.如果您的数据是数组,只要数据是连续的,*accessses*应该花费相同的时间.如果你有几个小的数据位于ram中,那么堆栈将会有更快的时间. (15认同)

Mad*_* Vi 9

有关于此主题的博客文章stack-allocation-vs-heap-allocation-performance-benchmark显示了分配策略基准。测试是用 C 编写的,并在纯分配尝试和使用内存初始化的分配之间执行比较。在不同的总数据大小下,执行循环次数并测量时间。每个分配由 10 个不同大小的 alloc/init/free 块组成(总大小如图表所示)。

测试在 Intel(R) Core(TM) i7-6600U CPU、Linux 64 位、4.15.0-50-generic、Spectre 和 Meltdown 补丁禁用上运行。

没有初始化: 没有数据初始化的内存分配

使用初始化: 使用数据初始化分配内存

在结果中,我们看到没有数据初始化的纯分配有显着差异。堆栈比堆快,但请注意循环计数非常高。

在处理分配的数据时,堆栈和堆性能之间的差距似乎缩小了。在 1M malloc/init/free(或堆栈分配)循环中,每个循环尝试分配 10 次,就总时间而言,堆栈仅比堆领先 8%。


Jam*_*nze 6

你的问题确实没有答案; 这取决于你还在做什么.一般来说,大多数机器在整个过程中使用相同的"内存"结构,因此无论变量驻留在何处(堆,堆栈或全局内存),访问时间都是相同的.另一方面,大多数现代机器具有分层内存结构,具有内存管道,多级缓存,主内存和虚拟内存.根据之前在处理器上发生的事情,实际访问可能是这些中的任何一个(无论是堆,堆栈还是全局),并且这里的访问时间差别很大,如果内存是单个时钟如果系统必须转到磁盘上的虚拟内存,则在管道中的正确位置,大约10毫秒.

在所有情况下,关键是地方.如果访问"接近"以前的访问,则会极大地提高在更快的位置之一找到它的机会:例如,缓存.在这方面,将较小的对象放在堆栈上可能会更快,因为当您访问函数的参数时,您可以访问堆栈内存(使用Intel 32位处理器,至少---使用更好的处理器,参数更可能在寄存器中).但是当涉及到数组时,这可能不是问题.


bob*_*bah 5

在堆栈上分配缓冲区时,优化范围不是访问内存的成本,而是消除堆上通常非常昂贵的动态内存分配(堆栈缓冲区分配可以被认为是瞬时的,因为整个堆栈是在线程启动时分配的) .


Gum*_*een 5

值得一提的是,下面代码中的循环——它只是读取和写入大数组中的每个元素——当数组在堆栈上时,它在我的机器上的运行速度始终比在堆上时快 5 倍(GCC,Windows 10, -O3 标志),即使在重新启动后(当堆碎片最小化时):

const int size = 100100100;
int vals[size]; // STACK
// int *vals = new int[size]; // HEAP
startTimer();
for (int i = 1; i < size; ++i) {
    vals[i] = vals[i - 1];
}
stopTimer();
std::cout << vals[size - 1];
// delete[] vals; // HEAP
Run Code Online (Sandbox Code Playgroud)

当然,我首先必须将堆栈大小增加到 400 MB。请注意,需要在末尾打印最后一个元素,以防止编译器优化所有内容。

  • @PaimanRoointan 在linux下,你可以使用`ulimit -s` (2认同)