我知道这听起来像是一个普遍的问题,而且我已经看过许多类似的问题(无论是在网上还是在网上),但没有一个问题真的像我的困境。
说我有这段代码:
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;
}
如果我在全局声明缓冲区[1024],我会获得任何性能吗?
我通过time命令在unix上运行了一些测试,并且执行时间之间几乎没有差异。
但我并不是真的相信......
理论上,这种变化是否会产生影响?
答案 0 :(得分:35)
访问堆中的数据的速度要快于堆栈中的数据吗?
并非本质上......在我曾经工作过的每个架构上,所有进程“内存”都可以按照相同的速度运行,具体取决于CPU缓存/ RAM /交换文件的级别当前数据以及该内存上的操作可能触发的任何硬件级同步延迟使其对其他进程可见,并包含其他进程/ CPU(核心)的更改等。
操作系统(负责页面错误/交换)以及硬件(CPU)捕获对已换出或未访问页面的访问,甚至不会跟踪哪些页面是“堆栈”与“堆“...内存页面是一个内存页面。也就是说,全局数据的虚拟地址可以在编译时计算和硬编码,基于堆栈的数据的地址通常是堆栈指针相对的,而堆上的内存几乎总是必须使用指针访问,这可能是在一些系统上稍微慢一些 - 它取决于CPU寻址模式和周期,但它几乎总是微不足道 - 除非你写的是百万分之一秒的东西非常重要,否则它甚至不值得一看或者不值得。
无论如何,在你的例子中,你将全局变量与函数本地(堆栈/自动)变量进行对比......没有涉及堆。堆内存来自new
或malloc
/ realloc
。对于堆内存,值得注意的性能问题是应用程序本身正在跟踪在哪些地址中使用了多少内存 - 所有需要一些时间来更新的记录作为指向内存的指针由{{1}分发} / new
/ malloc
,还有更多时间来更新,因为指针为realloc
d或delete
d。
对于全局变量,内存分配可以在编译时有效完成,而对于基于堆栈的变量,通常有一个堆栈指针,它由编译时计算的局部变量大小的总和(以及一些内务数据)递增)每次调用一个函数。因此,当调用free
时,可能需要一些时间来修改堆栈指针,但是如果没有main()
它可能只是被不同的数量修改而不是被修改,如果有,则修改它,所以根本没有运行时性能上的差异。
答案 1 :(得分:25)
堆栈更快,因为访问模式使得从中分配和释放内存变得微不足道(指针/整数简单地递增或递减),而堆中涉及更复杂的簿记。分配或免费。此外,堆栈中的每个字节都经常被频繁地重用,这意味着它往往被映射到处理器的缓存,使其非常快。堆的另一个性能影响是堆(主要是全局资源)通常必须是多线程安全的,即每个分配和释放需要 - 通常 - 与程序中的“所有”其他堆访问同步。
答案 2 :(得分:6)
你的问题确实没有答案;这取决于什么 否则你在做。一般来说,大多数机器都使用 相同"记忆"整个过程的结构,所以无论如何 变量所在的位置(堆,堆栈或全局内存), 访问时间将是相同的。另一方面,最现代 机器具有分层内存结构,带有内存 管道,几级缓存,主内存和虚拟 记忆。取决于之前发生的事情 处理器,实际访问可以是这些中的任何一个 (无论是堆,堆栈还是全局),以及 这里的访问时间差异很大,从单个时钟开始 内存在管道中的正确位置 如果系统必须转到虚拟内存,则大约10毫秒 在磁盘上。
在所有情况下,关键是地点。如果访问权限在"附近" 以前的访问,你大大提高了找到它的机会 例如,在一个更快的位置:缓存。在这 关注,将较小的物体放在堆叠上可能会更快, 因为当你访问函数的参数时,你就是这样 访问堆栈内存(使用Intel 32位处理器,at 至少---使用设计更好的处理器,参数更多 可能在寄存器中)。但这可能不是一个 涉及数组时的问题。
答案 3 :(得分:2)
在堆栈上分配缓冲区时,优化范围不是访问内存的成本,而是消除堆上通常非常昂贵的动态内存分配(堆栈缓冲区分配可以立即考虑,因为整个堆栈分配在线程启动)。
答案 4 :(得分:2)
就其价值而言,以下代码中的循环(仅读取和写入大数组中的每个元素)在数组位于堆栈上时与在堆上时相比,在我的机器上始终比原来快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
当然,我首先必须将堆栈大小增加到400 MB。请注意,需要在最后打印最后一个元素,以防止编译器优化所有内容。
答案 5 :(得分:2)
有关此主题stack-allocation-vs-heap-allocation-performance-benchmark的博客文章可用,其中显示了分配策略基准。测试用C语言编写,并在纯分配尝试与使用内存init进行分配之间进行比较。在不同的总数据大小下,执行循环数并测量时间。每个分配由10个不同的alloc / init / free块组成,这些块具有不同的大小(总大小显示在图表中)。
测试在Intel(R)CoreTM i7-6600U CPU,Linux 64位,4.15.0-50通用,Spectre和Meltdown补丁已禁用的情况下运行。
在结果中,我们看到在没有数据初始化的情况下,纯分配存在显着差异。堆栈比堆快,但请注意,循环计数超高。
在处理分配的数据时,堆栈和堆性能之间的差距似乎正在减小。在1M malloc / init / free(或堆栈分配)循环中,每个循环尝试10次分配时,就总时间而言,堆栈仅比堆高8%。
答案 6 :(得分:-2)
赋予在堆上声明的变量和变量数组更慢只是一个事实。以这种方式思考;
全局创建的变量分配一次,并在程序关闭后解除分配。对于堆对象,每次运行函数时都必须在现场分配变量,并在函数末尾取消分配..
曾经尝试在函数中分配对象指针吗?好吧,在函数退出之前更好地自由/删除它,你将会遇到一个记忆漏洞,你不会在解析器中被释放/删除的类对象中这样做。
当访问数组时,它们的工作方式相同,内存块首先由sizeof(DataType)*元素分配。稍后可以通过 - &gt;
访问1 2 3 4 5 6
^ entry point [0]
^ entry point [0]+3