页面错误和.Net编程

时间:2011-09-15 11:39:55

标签: .net c paging

我正在读一本关于操作系统的书,它给出了一些我最常理解的C语言示例。我现在看的例子显示了两个几乎相同的代码片段,它们将在一个虚构的系统上运行......

int i, j;
int [128] [128] data;

for (j = 0; j < 128; j++)
    for (i = 0; i < 128; i++)
        data [i] [j] = 0;

第二段代码

int i, j;
int [128] [128] data;

for (i = 0; i < 128; i++)
    for (j = 0; j < 128; j++)
        data [i] [j] = 0;

在这个特定的系统中,第一部分代码会导致16k页面错误,而第二部分只导致128页。

如果这是一个愚蠢的问题,我很抱歉,但在我使用.NET的经历中,我一直都没有意识到内存。我只是创建一个变量,它在某个地方,但我不知道在哪里,我不在乎。

我的问题是,.NET如何与这个虚构系统中的这些C示例进行比较(页面大小为128个字,数组的每一行占用一整页。在第一个示例中,我们在第1页设置了一个int,然后是第2页上的一个int,等等......第二个例子设置了第1页的所有整数,然后是第2页的所有整数,等等......)

此外,虽然我认为我理解为什么代码会产生不同级别的分页,但是我能用它做些什么呢?页面的大小是否取决于操作系统?作为一般的经验法则,尽可能连续地访问数组中的内存是否会被带走?

3 个答案:

答案 0 :(得分:7)

虚拟操作系统可用于演示原理,但我知道实际操作系统并非如此。如果页面在使用后立即被取消映射,则只能获得16K页面错误。虽然技术上可能会发生这种情况,但机器必须处于主要负载之下,迫切需要RAM来支持一组正在运行的进程。那时你只是不再关心perf,无论如何机器都会慢下来爬行。技术术语是“thrashing”。

在这段代码中你还没有提到更重要的事情。 1级CPU缓存在访问数组元素的速度方面起着重要作用。在第一次访问时从内存中填充高速缓存行(通常为64字节)。在下一个内存地址访问下一个元素非常便宜,数据已经在缓存中。访问外部索引首先更改的数组是非常昂贵的,它需要另一个高速缓存行填充,这可能需要数百个周期,最坏的情况。由于驱逐包含阵列数据的现有缓存行的风险很大,因此第一级缓存不是很大。要做到这一点,需要了解运行时如何在内存中对数组元素进行排序。

答案 1 :(得分:2)

你的两个样本是相同的,但大概你想要的是:

int i, j;
int [128] [128] data;

for (i = 0; i < 128; i++)
    for (j = 0; j < 128; j++)
        data [i] [j] = 0;  // <--- i,j

第二段代码

int i, j;
int [128] [128] data;

for (i = 0; i < 128; i++)
    for (j = 0; j < 128; j++)
        data [j] [i] = 0;   // <--- j,i

假设页面大小为128 * sizeof(int):

  • 在一种情况下,您将按顺序迭代数组。在这种情况下,最坏情况的页面错误数是pages = 1中数组的大小。

  • 在另一种情况下,您将在每次迭代时跳转到新页面,因此在最坏的情况下,您可能会在循环的每次迭代中出现页面错误。

.NET中的情况完全相同。

答案 2 :(得分:0)

您获得的页面错误数量取决于您访问的页面数量,这些页面不在内存中。

堆栈上的页面更有可能在内存中,除非这是第一次使用堆栈达到这个程度。

假设您的页面大小与许多系统上的页面大小相同,并且没有任何页面在内存中,您将访问128 * 128 * 4字节或64 KB,即16页。注意:如果此结构不在页面边界上开始,则该结构可能跨越17页,在这种情况下,第一页将在内存中。

只要您访问每个页面,访问它们的方式或顺序都无关紧要。


可能是您在谈论缓存未命中,而您访问数据的顺序可能会有所不同。在这种情况下,最好沿最后一个索引逐步访问数据。即对于a[i][j],您希望内循环使用j但是如果您的缓存足够大,这可能无关紧要。对于不适合快速缓存的真正大型结构,它可以有很大的不同。