计算二维数组的页面错误数

Posted

技术标签:

【中文标题】计算二维数组的页面错误数【英文标题】:Calculating number of page faults for 2-d array 【发布时间】:2013-04-04 09:41:16 【问题描述】:

我正在努力学习考试。我找到了这个例子,但不明白他们是如何得到答案的。谁能解释一下?

问题:

考虑二维数组A: int A[][] = 新的 int[100][100]; 其中 A[0][0] 位于页面大小为 200 的分页内存系统中的位置 200。操作矩阵的小进程驻留在页面 0(位置 0 到 199)中。因此,每条指令都将从第 0 页开始。 对于两个页框,以下数组初始化循环会产生多少页错误,使用 LRU 替换并假设第一个页框包含进程而另一个页框最初为空?

答:

for (int j=0;j<100;j++)
   for (int i=0; i<100; i++)
     A[i][j] = 0;

乙:

for(int i=0; i<100; i++)
  for (int j=0; j<100; j++)
      A[i][j] = 0;

给出的正确答案是: 一个:100 x 50 = 5000 乙:50

我有点理解第一部分。共有50页。 (10000/200=50) 并且每次 j 发生变化时,都会发生页面错误..所以总共有 100 个页面错误..但是为什么要乘以 50?为什么第二个是50?

谢谢!!

【问题讨论】:

这是一个措辞不当的问题。 1) 没有系统会使用 200 字节的页面(小而不是 2 的幂。2)两个帧在 VM 系统中是不现实的。 3) 它假定 C 风格的数组布局。这种非连续访问数组元素的问题在 CPU 缓存的上下文中更为相关。 【参考方案1】:

假设您的系统为您的进程分配了两个帧,这样矩阵的200 * sizeof(int) 可以一次保存在内存中。矩阵的分配发生在Row Major Order。

在第一个循环中A

for (int j=0;j<100;j++)
   for (int i=0; i<100; i++)
     A[i][j] = 0;

循环访问矩阵列的内存单元,如:

A[0][0], A[2][0], A[3][0], ...A[0][2], A[0][3], A[0][4], ......
  ^        ^        ^   
      row changes               

在每次迭代行更改,分配主要是行,每行占用一页。所以代码A 将导致每个替代A[i][j] 访问的页面错误,所以页面错误的总数= 100 * 100 / 2) = 5000。

第二个代码B

for(int i=0; i<100; i++)
  for (int j=0; j<100; j++)
      A[i][j] = 0;

在每次迭代中逐行循环访问矩阵的内存单元,例如:

A[0][0], A[0][5], A[0][6],...,A[1][0], A[1][7], A[1][8],...,A[2][0], A[2][9],
     ^        ^        ^  
  column changes, row are same 

逐行访问(读取时列更改仅在读取 100 次后更改行),一次加载一行,因此当行更改(对于外部循环)和每个替代行访问时会发生页面错误发生页面错误,因此页面错误数 = 100/2 = 50。

我们可以用另一种方式来理解它,例如: 在主要行中,我们需要新页面访问的行索引更改次数,因为页面数量很小,它在第一个 A 循环行索引更改的每个替代索引上出现页面错误,其中 B 循环行索引更改 100 次次,所以 A/B 中的缺页率 = 100*100/100 = 100,如果 A = 50,00 中发生缺页数,则 B 中缺页数 = 50,00/100 = 50。

同样,您可以计算Column-major order 的页面错误数,因为矩阵具有相同的行数,并且 cols 结果将相同。

我的书中给出了类似的例子: 下载 pdf:operating system book Galvin 阅读第 9 章:虚拟内存部分:9.9.5 程序结构。

【讨论】:

我有一个问题要问 Grijesh,你为什么要把 10,000 和 100 分成 2? (10,000/2 和 100/2)。你从哪里得到的 2? 因为换帧时发生了page fault,所以系统中的page frame的通知数是两个。--进一步理解它认为只有一个page frame时会发生什么,然后想想当page frame只有一个时会发生什么页面框架是两个。-如果需要解释,请告诉我,我会更新答案。【参考方案2】:

这里的关键是查看从线性内存地址读取时所有数组访问的外观。还必须假设行优先 (C) 顺序才能使答案有意义。该问题还遗漏了单位,我们假设它们是字节(因此 A 必须以 1 字节的类型保存)。

char *B = &(A[0][0]); (memory address 200)

访问A[i][j] 现在等同于B[i*100 + j]*(200 + i*100+j)(行优先顺序)。 两页可以放入内存。一个由程序获取(字节 0-199 - 也是 C 约定)。另一种是访问A,跨越100*100字节/(200字节/页)=50页。

由于 0-199 始终在内存中,因此另一页将寻址 n*200 到 (n+1)*200-1,其中 n 是某个整数 - 一次对应于 A 的 2 行。

最后,在页面错误时,最近最少使用 (LRU) 算法将刚刚从页面 0-199 读取一条指令,因此将始终丢弃保存 A 的旧部分的页面以读取 A 的新部分 (当 n 改变时,每 2 行)。

因此,您可以通过读取 100x100 矩阵的行来轻松查看发生了什么——每 2 行交换一个页面,并且在外部循环中重复 100 次(从左到右跨行)。这会导致 100x50 的页面错误。

在现实世界中,您可以使用linux commands 或getrusage 跟踪页面错误。

【讨论】:

【参考方案3】:

因此,二维数组中总共有 50 页,逐行或逐列存储。

如果您逐行存储页面并逐行访问它们,那么您将一遍又一遍地访问同一页面直到您到达下一页,因此您仅切换页面(导致页面错误)50次因为有 50 页。

如果您逐行存储页面,并逐列(或虎钳)访问它们,您将从一个页面获取一个元素,切换页面,从另一页面获取一个元素,切换页面等。所以您的仍然浏览 50 页,但每页切换 100 次。

想象你正在阅读一篇论文。如果您一次阅读一页,那么您将每页翻一次。如果您从每一页中读取一行,然后重新开始并阅读第二行,第三行等。您将不得不一遍又一遍地翻阅整张纸,对于页面上的每一行(假设所有页面的编号相同)像数组一样的行数)...每次翻页都是页面错误。

【讨论】:

【参考方案4】:

让我们看一个人为但内容丰富的示例。假设页面是 大小为 128 个字。考虑一个 C 程序,其功能是初始化为 0 128×128 数组的每个元素。以下代码是典型的:

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

请注意,该数组存储的是主行;也就是存储数组 数据[0][0]、数据[0][1]、···、数据[0][127]、数据[1][0]、数据[1][1]、···、 数据[127][127]。对于 128 个单词的页面,每行占一页。因此, 前面的代码将每一页中的一个字归零,然后在每一页中将另一个字归零 页等。如果操作系统分配的帧数少于 128 整个程序,那么它的执行将导致 128 × 128 = 16,384 个页面错误。

相反,假设我们将代码更改为

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

此代码在开始下一页之前将一页上的所有单词归零, 将缺页次数减少到 128 个。

【讨论】:

该概念取自《操作系统概念要点》一书

以上是关于计算二维数组的页面错误数的主要内容,如果未能解决你的问题,请参考以下文章

C程中如何计算数组(一维及二维)占内存空间的大小

回溯法计算二维数组最短路径

二维数组求和

数组(随机生成一维数组),二维数组的概念和题目设计(利用二维数组 :任意给定分数去计算科目和人均平均分)

将字符串数组分配给二维字符串数组

求C语言二维数组元素排列组合?