Linux 中的堆栈内存在物理上是否连续?

Is stack memory contiguous physically in Linux?

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

编辑:

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

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

不,不保证物理地址的连续性。不过没关系,因为user-space程序不使用物理地址,所以不知道是这样。

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

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

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

这是一个复杂的话题。

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

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

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

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

As far as I can see, stack memory is contiguous in virtual memory address, but stack memory is also contiguous physically? And does this have something to do with the stack size limit?

不是,栈内存在物理地址上不一定是连续的space。它与堆栈大小限制无关。这与 OS 如何管理内存有关。 OS 仅在第一次访问相应的虚拟页面时(或自从它被调出到磁盘后第一次访问)才分配物理页面。这称为 demand-paging,它有助于节省内存使用量。

why do we think that stack memory is always quicker than heap memory? If it's not physically contiguous, how can stack take more advantage of cache?

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

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

if we want to sort a large amount of numbers, using array to store the numbers is better than using a list, because every list node may be constructed by malloc, so it may not take good advantage of cache, that's why I say stack memory is quicker than heap memory.

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

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