数组在*物理*内存中是否是连续的?

8

我知道数组元素在虚拟内存中是连续的,但它们在物理内存中也是这样吗?

#define N 100*1024*1024
int arr[N];

请注意,到目前为止,大多数人都说答案是否定的,但我的主要问题如下粗体所示。
如果不是这样,至少如果在页面中找到一个元素,那么我可以假设整个页面都填充了数组元素(换句话说,它们可能不连续,而是在不同页面中,但在每个单独的页面中是连续的,因此当读取1个元素时,我们读取附近元素的整个页面,即4096字节,而不是为下一个元素读取另一页,从而提高性能)?
如果是,请问,如果我试图分配一个大数组,而没有可用的连续物理内存(我认为这经常会发生),该怎么办?
如果答案取决于编程语言,我对C和C ++感兴趣,如果它取决于操作系统,我对linux及其变体(如ubuntu)感兴趣。

10
在虚拟内存系统中,物理内存对于程序是不可见的,作为一名程序员,你不需要考虑它。我的翻译是否符合您的要求? - 500 - Internal Server Error
1
@daniel 是的,它确实如此。"在页面内连续的所有内容都将在虚拟内存和物理内存中连续". 这是您得到的唯一保证。 - GSerg
1
连续和非连续内存之间没有性能差异。这就是随机访问的含义。 - stark
2
@GSerg,那不是我问的问题...我问的是如果它们是连续的物理内存,它们是否会在同一页上连续...A是B并不意味着B是A。 - user16363840
2
@GSerg 不是“物理内存中连续的项目将”,这不是我问的。我问的是,如果它们在虚拟内存中是连续的,那么... - user16363840
显示剩余14条评论
3个回答

4
在 Linux x86 上,连续的虚拟内存以 4KB 的页面分配:

根据定义,页面内的内存是连续的。但是物理页面可以以任何顺序映射。这使得操作系统可以分配和释放任意数量的虚拟内存,只要有可用的物理页面(或交换空间)。页面级别的碎片对CPU或缓存不构成问题,但可能会影响分配性能,因此Linux页面分配算法不断发展,如本文所述

这意味着一个大的连续数组将以4 KB的块(对齐到4 KB)的形式驻留在RAM中。第一个和最后一个块可能占据页面的一部分,其余部分将占据完整页面。


除了2以外,对于“如果没有,至少如果在页面中找到一个元素,那么我可以假设整个页面填充了数组元素”的答案是正确的吗? - user16363840
@daniel 是的,一个数组元素可以填满整个页面。 - rustyx

1
每个虚拟内存页面都与一个物理内存页面完全相同;对于小于一页的单元,不进行重新映射。这是分页原则固有的特性。假设使用4KB的页面大小,则在32位或64位地址中查找顶部的20位或52位以识别物理页面,并将低12位用作该物理页面中的偏移量。因此,如果您具有在同一虚拟内存页面中的两个地址(即虚拟地址仅在其12个低位上不同),则它们将位于某个单一物理内存页面中的相同相对偏移量处。(假设虚拟页面实际上由物理内存支持;当然,它随时可能被交换出去。)
对于不同的虚拟页面,根本没有关于它们如何映射到物理内存的任何保证。它们很容易映射到物理内存的完全不同位置(或者当然可以交换其中一个或两个)。
如果在虚拟内存中分配了一个非常大的数组,则无需提供足够大的连续物理内存块;操作系统可以将这些虚拟内存页面映射到任意物理内存页面。(或者更可能的是,它最初会保持页面未映射状态,然后按小块分配物理内存,当您触摸页面并触发页面错误时。)
这适用于进程虚拟内存的所有部分:静态代码和数据、堆栈、使用malloc/sbrk/mmap等动态分配的内存等。
Linux确实支持巨大页面,huge pages,在这种情况下,相同的逻辑适用,但页面更大(几MB或GB;可用大小由硬件固定)。
除了像硬件DMA这样的非常专业化的应用程序之外,通常没有任何理由让应用程序员关心物理内存在幕后是如何排列的。

因此,如果您在虚拟内存中分配一个非常大的数组,则不需要有足够大的连续物理内存块可用。这难道不会提高性能吗?如果我正在读取数组的值,我只需要找到第一个物理页,然后检查下一页,依此类推,但如果这种情况不发生,则需要重新查找下一页,这将需要一些时间。 - user16363840
@daniel:不完全是这样。由于页面可以任意放置,因此当您访问下一页时,CPU必须执行新的页面表遍历,以查看该页面在物理内存中的位置并相应地填充TLB。(我假设这就是您所说的“重新查找”。)它不能假定它将是下一个连续的物理页面,一旦页面遍历完成,无论它是否是下一个页面或其他页面都没有关系。 - Nate Eldredge

0
我知道数组元素在虚拟内存中是连续的,但它们在物理内存方面也是这样吗?
不是。你和我日常使用的所有电脑都配备了内存管理单元,以将虚拟地址转换为物理地址。地址是以称为页面的粒度块进行转换的,典型页面大小为4 kB。在页面内,物理地址将是连续的,但需要许多页面的数组很可能会完全分散并散布在任意的物理内存地址上。
即使物理地址是连续的,"物理"地址映射到RAM条上的真实芯片位置的方式取决于主板。
不必担心这点。
如果没有,至少如果在页面中找到一个元素,那么我是否可以假设整个页面被填满了数组元素(换句话说,它们在不同页面中可能不是连续的,但在每个单独页面中是连续的,因此在读取1个元素时,我们读取相邻元素的整个页面,即4096字节,而不是在下一页中读取其他元素,从而提高性能)?

如果页面之前不在RAM中,访问其中的任何部分都将执行昂贵的操作,将整个页面从操作系统的页面文件等中拉入RAM中(操作系统可能还会预取一些后续页面)。

这仍然并不意味着RAM中页面的每个部分都可以以相同的速度访问,因为您仍然受到CPU缓存的影响,而访问缓存甚至比访问主板上的RAM更快。

典型的CPU缓存行大小为64或128字节,例如,比页面小得多。但是,访问缓存行的一部分肯定会将整个行拉入缓存中。

另请参见:https://igoro.com/archive/gallery-of-processor-cache-effects/


“即使物理地址是连续的,但“物理”地址映射到RAM芯片上真正的位置的方式取决于主板。这让我很感兴趣,难道“物理”不是RAM上真正的芯片位置吗?我在哪里可以阅读更多相关信息?” - user16363840
1
物理地址是内核(和CPU)在请求从内存控制器获取数据时发送到内存控制器的地址。内存控制器如何将这些物理地址映射到特定RAM芯片上的实际位置是它与SIMM之间的私有实现细节。 - Jeremy Friesner
有什么链接可以让我了解更多关于这个新的低层技术吗?谢谢。 - user16363840
1
@daniel 实际物理内存使用“bank”,“row”和“column”进行寻址。我对此不是很了解,但这些术语应该可以给你一些搜索关键词。内存块的读写被分配在RAM插槽上所有芯片之间,使得芯片可以并行工作,因此它是另一种物理分段的级别。 - Boann
1
@daniel:看看Ulrich Drepper的优秀论文,"程序员应该了解的有关内存的一切" - Nate Eldredge

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