6

据我所知,堆栈内存在虚拟内存地址中是连续的,但堆栈内存在物理上也是连续的?这与堆栈大小限制有关吗?

编辑:

我曾经认为栈内存在物理上不一定是连续的,但是为什么我们认为栈内存总是比堆内存快呢?如果它在物理上不是连续的,堆栈如何更好地利用缓存?还有一件事总是让我感到困惑,cpu在数据段中执行指令,它不在虚拟内存中的堆栈段附近,我认为操作系统不会使堆栈段和数据段在物理上彼此靠近,所以这可能会损害缓存效果,您怎么看?

再次编辑: 也许我应该举一个例子来更好地表达自己,如果我们想对大量数字进行排序,使用数组来存储数字比使用列表更好,因为每个列表节点都可能由 构造malloc,所以它可能没有充分利用缓存,这就是为什么我说堆栈内存比堆内存快。

4

4 回答 4

8

据我所知,堆栈内存在虚拟内存地址中是连续的,但堆栈内存在物理上也是连续的?这与堆栈大小限制有关吗?

不,堆栈内存在物理地址空间中不一定是连续的。它与堆栈大小限制无关。这与操作系统如何管理内存有关。操作系统仅在第一次访问相应的虚拟页面(或自从它被分页到磁盘后第一次访问)时才分配一个物理页面。这称为需求分页,它有助于节省内存使用量。

为什么我们认为栈内存总是比堆内存快?如果它在物理上不是连续的,堆栈如何更好地利用缓存?

它与缓存无关。从堆栈分配和释放内存比堆更快。这是因为从堆栈分配和释放只需要一条指令(递增或递减堆栈指针)。另一方面,从堆中分配和/或解除分配内存涉及更多工作。有关更多信息,请参阅本文

现在,一旦分配了内存(从堆或堆栈),访问该分配的内存区域所花费的时间并不取决于它是堆栈内存还是堆内存。这取决于内存访问行为以及它是否对缓存和内存架构友好。

如果我们要对大量的数字进行排序,使用数组存储数字比使用列表更好,因为每个列表节点都可能由malloc构造,所以它可能没有很好地利用缓存,这就是为什么我说堆栈内存比堆内存快。

使用数组更快不是因为数组是从堆栈中分配的。可以从任何内存(堆栈、堆或任何地方)分配数组。它更快,因为数组通常一次连续访问一个元素。当第一个元素被访问时,包含该元素和其他元素的整个高速缓存行从内存中提取到 L1 高速缓存。因此访问该缓存行中的其他元素可以非常有效地完成,但访问缓存行中的第一个元素仍然很慢(除非缓存行是预取的)。这是关键部分:由于缓存行是 64 字节对齐的,并且虚拟页面和物理页面也是 64 字节对齐的,因此可以保证任何缓存行完全驻留在单个虚拟页面和单个物理页面中. 这使得获取缓存行变得高效。同样,所有这些都与数组是从堆栈还是堆中分配无关。无论哪种方式,它都是正确的。

另一方面,由于链表的元素通常不连续(甚至在虚拟地址空间中也不连续),因此包含一个元素的高速缓存行可能不包含任何其他元素。因此,获取每个元素可能会更昂贵。

于 2018-04-05T23:41:52.287 回答
3

记忆就是记忆。堆栈内存不比堆内存快,也不慢。都是一样的。使内存成为堆栈或堆的唯一因素是应用程序如何分配它。完全可以在堆上分配内存并使其成为程序堆栈。

速度差异在于分配。堆栈内存是通过从堆栈指针中减去:一条指令来分配的。

分配堆的过程取决于堆管理器,但它要复杂得多,可能需要将页面映射到地址空间。

于 2018-04-01T12:26:00.713 回答
2

不,不保证物理地址的连续性。但这没关系,因为用户空间程序不使用物理地址,所以不知道是这种情况。

于 2018-04-01T05:15:24.283 回答
1

这是一个复杂的话题。

堆和堆栈(通常)具有相同的内存和内存类型(MTRR、每页缓存设置等)。[mmap、文件、驱动程序可能有不同的策略,或者当用户显式更改它时]。

堆栈可能会更快,因为它经常被使用。当你调用一个函数时,参数和局部变量被放入堆栈,所以缓存是新鲜的。此外,由于函数调用和返回频繁,可能在其他缓存级别中还有一些堆栈,并且很少对堆栈顶部进行分页(因为它是最近使用的)。

因此缓存可能会更快,但前提是您的变量很少。如果您允许堆栈上的大型数组,例如 with alloca,则优势消失。

总的来说,这是一个非常复杂的话题,最好不要优化太多,因为这会导致代码复杂,因此更难重构和代码的高级优化。(例如在多维数组上,索引(以及内存)和循环的顺序可以提高速度,但很快代码将无法维护)。

于 2018-04-01T16:19:25.233 回答