【分析解答】

第 3 版 · OCR 辅助 · 原书检索 · 文字用于定位,公式与图表请回原页核验 · 本次导入快照

## 【分析解答】

对于函数 clear1,其数组访问顺序与在内存的存放顺序完全一致,因此,空间局部性最好。

对于函数 clear2,其数组访问顺序在每个数组元素内跳跃式访问,相邻两次访问的单元最大相差3个int型变量(假定sizeof(int) = 4,则相当于12B),因此空间局部性比clear1差。若主存块大小比12B小,则大大影响命中率。

对于函数 clear3,其数组访问顺序与在内存的存放顺序不一致,相邻两次访问的单元都相差6个int型变量(假定sizeof(int) = 4,则相当于24B)因此,空间局部性比clear2还差。若主存块大小比24B小,则大大影响命中率。

26. 以下是计算两个向量点积的程序段:

float dotproduct(float x[8], float y[8])
{
float sum = 0.0;
int i;
for (i = 0; i < 8; i++)
sum += x[i] * y[i];
return sum;
}
请回答下列问题: (1)访问数组 $x$ 和 $y$ 时的时间局部性和空间局部性各如何?你能否推断出命中率的高低? (2)假定数据 cache 采用直接映射方式,数据区容量为 32B,每个主存块大小为 $16\mathrm{B}$;编译器将变量 sum 和 $i$ 分配在寄存器中,数组 $x$ 存放在 $00000040\mathrm{H}$ 开始的 32B 的连续存储区中,数组 $y$ 则紧跟在 $x$ 后进行存放。该程序数据访问的命中率是多少?要求说明每次访问时 cache 的命中情况。 <!-- page: 230 --> (3) 将上述(2)中的数据 cache 改用 2 路组相联映射方式,块大小改为 8B,其他条件不变,则该程序数据访问的命中率是多少? (4) 在上述(2)中条件不变的情况下,将数组 $x$ 定义为 float $x[12]$,则数据访问的命中率是多少? 【分析解答】 (1) 数组 $x$ 和 $y$ 都按存放顺序访问,空间局部性都较好,但每个数组元素都只被访问一次,故没有时间局部性。命中率的高低与 cache 容量、块大小、映射方式等均有关,而题干中没有给出这些信息,因此,无法推断命中率的高低。 (2) cache 共有 $32\mathrm{B} / 16\mathrm{B} = 2$ 行;4 个数组元素占一个主存块;数组 $x$ 的 8 个元素(共 32B)分别存放在主存 $40\mathrm{H}$ 开始的 32 个单元中,共有 2 个主存块,其中 $x[0] \sim x[3]$ 在第 4 块,$x[4] \sim x[7]$ 在第 5 块;数组 $y$ 的 8 个元素分别在主存第 6 块和第 7 块。所以,$x[0] \sim x[3]$ 和 $y[0] \sim y[3]$ 都映射到 cache 第 0 行;$x[4] \sim x[7]$ 和 $y[4] \sim y[7]$ 都映射到 cache 第 1 行。因为 $x[i]$ 和 $y[i]$ 总是映射到同一个 cache 行,相互淘汰对方,故每次都不命中,命中率为 0。 (3) 改用 2 路组相联,块大小改为 8B,则 cache 共有 4 行,每组两行,共两组。两个数组元素占一个主存块。数组 $x$ 占 4 个主存块,$x[0] \sim x[1]$、$x[2] \sim x[3]$、$x[4] \sim x[5]$、$x[6] \sim x[7]$ 分别在第 8~11 块;数组 $y$ 占 4 个主存块,$y[0] \sim y[1]$、$y[2] \sim y[3]$、$y[4] \sim y[5]$、$y[6] \sim y[7]$ 分别在第 12~15 块;因为每组有两行,所以 $x[i]$ 和 $y[i]$ 可以存放到同一个 cache 组的不同 cache 行内,因此,不会发生冲突。每调入一块,装入的 2 个数组元素中,第 2 个数组元素总是命中,故命中率为 50%。 (4) 将数组 $x$ 定义为 12 个元素后,则 $x$ 共有 48B,使得 $y$ 从主存第 7 块开始存放,即 $x[0] \sim x[3]$ 在第 4 块,$x[4] \sim x[7]$ 在第 5 块,$x[8] \sim x[11]$ 在第 6 块中,$y[0] \sim y[3]$ 在第 7 块,$y[4] \sim x[7]$ 在第 8 块。因而,$x[i]$ 和 $y[i]$ 就不会映射到同一个 cache 行中。每调入一块,装入 4 个数组元素,第一个元素不命中,后面 3 个总命中,故命中率为 75%。 27. 以下是对矩阵进行转置的程序段:
typedef int array[4][4];
void transpose(array dst, array src)
{
int i, j;
for(i=0; i&amp;lt;4; i++)
for(j=0; j&amp;lt;4; j++)
dst[j][i]=src[i][j];
}
假设该段程序运行的计算机中 sizeof(int) = 4,且只有一级 cache,其中 L1 data cache 的数据区大小为 32B,采用直接映射、写回方式,块大小为 16B,初始为空。数组 dst 从地址 0000 C000H 开始存放,数组 src 从地址 0000 C040H 开始存放。仿照 col = 0,row = 0 栏目中的形式填写表 7.1,说明数组元素 src[row][col] 和 dst[row][col] 各自映射到 cache 哪一行,其访问是命中(hit)还是缺失(miss)。若 L1 data cache 的数据区容量改为 128B 时,重新填写表中内容。 <!-- page: 231 --> 表 7.1 题 27 中的 src 数组和 dst 数组 [tbl-44.md](tbl-44.md) 【分析解答】 从程序来看,数组访问过程如下。 src[0][0],dst[0][0],src[0][1],dst[1][0],src[0][2],dst[2][0],src[0][3],dst[3][0] src[1][0],dst[0][1],src[1][1],dst[1][1],src[1][2],dst[2][1],src[1][3],dst[3][1] src[2][0],dst[0][2],src[2][1],dst[1][2],src[2][2],dst[2][2],src[2][3],dst[3][2] src[3][0],dst[0][3],src[3][1],dst[1][3],src[3][2],dst[2][3],src[3][3],dst[3][3] 因为块大小为 16B,每个数组元素有 4B,所以 4 个数组元素占一个主存块,因此每次总是调入 4 个数组元素到 cache 的一行中。 当数据区容量为 32B 时,L1 data cache 中共有 2 行。因为地址 0000 C000H 和 0000 C040H 的最低 5 位都是 0,所以,数组元素 dst[0][i],dst[2][i],src[0][i],src[2][i](i = 0~3)都映射到 cache 第 0 行,数组元素 dst[1][i],dst[3][i],src[1][i],src[3][i](i = 0~3)都映射到 cache 第 1 行。因此,从上述访问过程来看,src[0][0] 所在的主存块(即存放 src[0][i](i = 0~3)中 4 个数组元素的主存块)刚调入 cache,dst[0][0] 所在主存块又把它替换掉了。 当数据区容量为 128B 时,L1 data cache 中共有 8 行。数组元素 dst[0][i],dst[1][i],dst[2][i],dst[3][i],src[0][i],src[1][i],src[2][i],src[3][i](i = 0~3)分别映射到 cache 第 0、1、2、3、4、5、6、7 行。因此,不会发生数组元素的替换。每次总是第一个数组元素不命中,后面三个数组元素都命中。 表 7.2 和表 7.3 给出了 cache 数据容量分别为 32B 和 128B 时数组 src 和 dst 的每个元素的命中情况。 表 7.2 题 27 中的 src 数组和 dst 数组在 cache 数据容量分别为 32B 时的命中情况 [tbl-45.md](tbl-45.md) <!-- page: 232 --> 表 7.3 题 27 中的 src 数组和 dst 数组在 cache 数据容量分别为 128B 时的命中情况 [tbl-46.md](tbl-46.md) 28. 通过对方格中每个点设置相应的 CMYK 值就可以将方格涂上相应的颜色。图 7.6 所示的三个程序段都可实现对一个 $8 \times 8$ 的方格中涂上黄颜色的功能。 [tbl-47.md](tbl-47.md) 图 7.6 题 28 中的伪代码程序 假设 cache 的数据区大小为 512B,采用直接映射,块大小为 32B,存储器按字节编址,sizeof(int) = 4。编译时变量 $i$ 和 $j$ 分配在寄存器中,数组 square 按行优先方式存放在 00000C80H 开始的连续区域中,主存地址为 32 位。要求: (1) 对三个程序段 A、B、C 中数组访问的时间局部性和空间局部性进行分析比较。 (2) 画出主存中的数组元素和 cache 中行的对应关系图。 (3) 计算三个程序段 A、B、C 中数组访问的写操作次数、写不命中次数和写缺失率。 【分析解答】 (1) 程序段 A、B 和 C 中,都是每个数组元素只被访问一次,所以都没有时间局部性;程序段 A 的访问顺序和存放顺序一致,所以空间局部性好;程序段 B 的访问顺序和存放顺序不一致,所以空间局部性不好;程序段 C 的访问顺序和存放顺序部分一致,所以空间局部性的优劣介于程序 A 和 B 之间。 (2) cache 的行数为 $512\mathrm{B} / 32\mathrm{B} = 16$;square 数组首地址为 $00000\mathrm{C}80\mathrm{H} = 0\cdots 0110010000000\mathrm{B}$,因为最低 5 位地址为全 0,说明 square 数组正好是主存第 $1100100\mathrm{B}(100)$ 块的起始地址。数组从主存第 100 块开始存放,一个数组元素占 $4\times 4\mathrm{B} = 16\mathrm{B}$,每 2 个数组元素占用 <!-- page: 233 --> 一个主存块。 $8 \times 8$ 的数组共占用 32 个主存块,正好是 cache 数据区大小的 2 倍。因为 100 mod $16 = 4$,所以主存第 100 块映射到的 cache 行号为 4。主存中的数组元素与 cache 行的映射关系如图 7.7 所示。 图 7.7 主存中数组元素与 cache 行的映射关系 (3)对于程序段 A:每两个数组元素(共涉及 8 次写操作)装入一个 cache 行中,总是第一次访问时未命中,后面 7 次都命中,所以总的写操作次数为 $64 \times 4 = 256$ 次,写不命中次数为 $256 \times 1/8 = 32$ 次,因而写缺失率为 $12.5\%$。 对于程序段 B:每两个数组元素(共涉及 8 次写操作)装入一个 cache 行中,但总是只有一个数组元素(涉及 4 次写操作)在被淘汰之前被访问,并且总是第一次不命中,后面 3 次命中。即写不命中次数为 $256 \times 1/4 = 64$ 次,因而写缺失率为 $25\%$。 对于程序段 C:第一个循环共访问 64 次,每次装入两个数组元素,第一次不命中,第二次命中;第二个循环,共访问 $64 \times 3$ 次,每两个数组元素(共涉及 6 次写操作)装入一个 cache 行中,并且总是第一次不命中,后面 5 次命中。所以,总的写不命中次数为 $32 + (3 \times 64) \times 1/6 = 64$ 次,因而总的写缺失率为 $25\%$。 29. 已知 cache1 采用直接映射方式,共 16 行,块大小为 1 个字,缺失损失为 8 个时钟周期;cache2 也采用直接映射方式,共 4 行,块大小为 4 个字,缺失损失为 11 个时钟周期。假定开始时 cache 为空,采用字编址方式。要求找出一个访问地址序列,使得 cache2 具有更低的缺失率,但总的缺失损失反而比 cache1 大。 ## 【分析解答】 假设 cache1 和 cache2 的缺失次数分别为 $x$ 和 $y$,根据题意,$x$ 和 $y$ 必须满足以下条件: $11 \times y &gt; 8 \times x$ 且 $x &gt; y$,显然,满足该条件的 $x$ 和 $y$ 有许多,例如,$x = 4$,$y = 3$ 或 $x = 5$,$y = 4$ 等。 对于以下的访问地址序列:0,1,4,8,cache1 缺失 4 次,而 cache2 缺失 3 次; 对于以下的访问地址序列:0,2,4,8,12,cache1 缺失 5 次,而 cache2 缺失 4 次; 对于以下的访问地址序列:0,3,4,8,12,16,20,cache1 缺失 7 次,而 cache2 缺失 6 次; <!-- page: 234 --> 如此等等,可以找出很多。 30. 提高关联度通常会降低缺失率,但并不总是这样。请给出一个地址访问序列,使得采用 LRU 替换算法的 2 路组相联映射 cache 比具有同样大小的直接映射 cache 的缺失率更高。 ## 【分析解答】 2 路组相联 cache 的组数是直接映射 cache 的行数的一半,所以,可以找到一个地址序列 A、B、C,使得 A 映射到某一个 cache 行,B 和 C 同时映射到另一个 cache 行,并且 A、B、C 映射到同一个 cache 组。这样,如果访存的地址序列为 A,B,C,A,B,C,A,B,C,…,则对于直接映射 cache,其命中情况为 miss/miss/miss/hit/miss/miss/hit/miss/miss/…命中率可达 33.3%;对于组相联 cache,因为 A、B、C 映射到同一个组,每组只有 2 行,采用 LRU 替换算法,所以,每个地址处的数据刚调出 cache 就又被访问到,每次都是 miss,命中率为 0。例如,假定直接映射 cache 为 4 行 × 1 字/行,同样大小的 2 路组相联 cache 为 2 组 × 2 行/组 × 1 字/行,当访问序列为 0,2,4,0,2,4,0,2,4,…(局部块大小为 3,大于每一组的行数)时,则出现上述情况。 当访问的局部块比组大时,可能会发生“颠簸(抖动)”现象:刚被替换出去的数据又被访问,导致缺失率为 100%。 31. 假定有三个处理器,分别带有以下不同的 cache。 cache1:采用直接映射方式,块大小为 1 个字,指令和数据的缺失率分别为 4% 和 6%。 cache2:采用直接映射方式,块大小为 4 个字,指令和数据的缺失率分别为 2% 和 4%。 cache3:采用 2 路组相联映射方式,块大小为 4 个字,指令和数据的缺失率分别为 2% 和 3%。 在这些处理器上运行相同的程序,该程序的 CPI 为 2.0,其中有一半是访存指令。若缺失损失为“块大小 + 6”个时钟周期,处理器 1 和处理器 2 的时钟周期都为 420ps,带有 cache3 的处理器 3 的时钟周期为 450ps。请问:哪个处理器因 cache 缺失而引起的额外开销最大?哪个处理器执行速度最快? ## 【分析解答】 假设所运行的程序共执行 N 条指令,每条访存指令仅读写一次内存数据,则在该程序执行过程中各处理器因 cache 缺失而引起的额外开销和执行时间计算如下。 对于处理器 1,额外开销为 $N \times (4\% + 6\% \times 50\%) \times (1 + 6) = 0.49N$ 个时钟周期,执行程序所需时间为 $(N \times 2.0 + 0.49N) \times 420ps = 1045.8Nps$。 对于处理器 2,额外开销为 $N \times (2\% + 4\% \times 50\%) \times (4 + 6) = 0.40N$ 个时钟周期,执行程序所需时间为 $(N \times 2.0 + 0.40N) \times 420ps = 1008Nps$。 对于处理器 3,额外开销为 $N \times (2\% + 3\% \times 50\%) \times (4 + 6) = 0.35N$ 个时钟周期,执行程序所需时间为 $(N \times 2.0 + 0.35N) \times 450ps = 1057.5Nps$。 由此可见,处理器 1 的 cache 缺失引起的额外开销最大,处理器 2 的执行速度最快。 32. 假定某处理器带有一个数据区容量为 256B 的 cache,主存块大小为 32B。以下 C 语言程序段运行在该处理器上,sizeof(int) = 4,编译器将变量 i、j、c、s 都分配在通用寄存器中,因此,只要考虑数组元素的访存情况,假定数组起始地址正好在一个主存块的开始处。若 cache 采用直接映射方式,则当 s = 64 和 s = 63 时,缺失率分别为多少?若 cache 采用 2 路组相联映射方式,则当 s = 64 和 s = 63 时,缺失率又分别为多少? <!-- page: 235 -->
int i, j, c, s, a[128];
...
for (i=0; i&amp;lt;10000; i++)
for (j=0; j&amp;lt;128; j=j+s)
c=a[j];
## 【分析解答】 因为块大小为 32B,数组起始地址正好是一个主存块的开始,所以每 8 个数组元素占一个主存块;cache 共有 $256\mathrm{B}/32\mathrm{B}=8$ 行,在 2 路组相联映射时,cache 有 4 组。以下仅考虑数组访问情况。 (1)直接映射,$s = 64$:访存顺序为 $a[0], a[64]; a[0], a[64];\cdots$,共循环 10000 次。因为 $a[0]$ 和 $a[64]$ 正好相差 256B,即相差 8 个主存块,因而除 8 同余,直接映射方式下,$a[0]$ 所在主存块和 $a[64]$ 所在主存块正好被映射到同一个 cache 行,因而每次都会发生冲突,其缺失率为 $100\%$。 (2)直接映射,$s = 63$:访存顺序为 $a[0], a[63], a[126]; a[0], a[63], a[126];\cdots$,循环 10000 次。因为 $a[63]$ 所在主存块的第一个数组元素 $a[56]$ 和 $a[126]$ 所在主存块的第一个数组元素 $a[120]$ 之间正好相差 256B,即相差 8 个主存块,因而除 8 同余,直接映射方式下,这两个元素正好都映射到同一个 cache 行,因此每次都会发生冲突,而 $a[0]$ 不会发生冲突,故缺失率约为 $67\%$。 (3)2 路组相联,$s = 64$:访存顺序为 $a[0], a[64]; a[0], a[64];\cdots$,共循环 10000 次。因为 $a[0]$ 和 $a[64]$ 之间正好相差 256B,即相差 8 个主存块,因而除 4 同余,在 2 路组相联映射方式下,这两个元素所在主存块会映射到同一个组,可放在同一组的不同 cache 行中,所以不会发生冲突,总缺失次数仅为 2 次,缺失率近似为 0。 (4)2 路组相联,$s = 63$:访存顺序为 $a[0], a[63], a[126]; a[0], a[63], a[126];\cdots$,共循环 10000 次。因为 $a[63]$ 所在主存块的第一个数组元素 $a[56]$ 和 $a[126]$ 所在主存块的第一个数组元素 $a[120]$ 之间正好相差 256B,即相差 8 个主存块,因而除 4 同余,这两个元素所在主存块被映射到同一个组,可放在同一组的不同 cache 行中,而 $a[0]$ 不会发生冲突,所以总缺失次数仅为 3 次,缺失率近似为 0。 33. 假定一个分页虚拟存储系统的虚拟地址为 40 位,物理地址为 36 位,页大小为 16KB,按字节编址。若页表中有有效位、访问权限位、修改位、使用位,共占 4 位,外存地址不在页表中,则该存储系统中每个进程的页表大小为多少?如果按计算出来的实际大小构建页表,则会出现什么问题? ## 【分析解答】 因为每页大小有 16KB,所以虚拟页数为 $2^{40}\mathrm{B}/16\mathrm{KB}=2^{(40-14)}=2^{26}$ 页。物理页面和虚拟页面大小相等,所以物理页号的位数为 $36-14=22$ 位。每个页表项包括有效位、访问权限位、修改位、使用位、物理页号等,所以其位数至少为 $4+22=26$。为了简化对页表项的访问,每个页表项取 32 位。因此,每个进程的页表大小为 $2^{26} \times 32\mathrm{b}=256\mathrm{MB}$。如果按实际计算出的页表大小构建页表,则构建出的页表会因为过大而导致页表无法一次装入内存。 34. 假定一个分页虚拟存储系统按字节编址,逻辑地址有 36 位,页大小为 16KB,物理地址位数为 32 位,页表中有效位和修改位各占 1 位、使用位和存取方式位各占 2 位,而且所 <!-- page: 236 --> 有虚拟页都在使用中。请问:每个进程的页表大小至少为多少?如果所使用的快表(TLB)中总的表项数为256项,采用2路组相联cache实现,则快表的大小至少为多少?