数组共有 24×64=1536 个 int,每个 int 占 4 B;每行占 64×4=256 B。按行优先存放时,任意元素的虚拟地址为
VA(a[i][j])=00422000H+(64i+j)×4.
计算页面和 Cache 时,要区分页面大小 4 KB 与主存块大小 32 B;一次缺页调入一页,一次 Cache 缺失处理一个主存块,两者不是同一事件。
(1)页面数量、缺页次数及故障地址
数组总容量为
24×64×4=6144 B.
起始地址 00422000H 是 4 KB 页边界,数组最后一个字节地址为
00422000H+6144−1=004237FFH.
因此数组覆盖两个页面:
| 页面地址范围 |
数组占用部分 |
原循环首次访问该页的元素 |
页故障地址 |
| 00422000H~00422FFFH |
第 0~15 行,共 4096 B |
a[0][0] |
00422000H |
| 00423000H~00423FFFH |
第 16~23 行,共 2048 B |
a[16][0] |
00423000H |
数组执行前不在主存,第一次访问每页会缺页;调入后题设保证不置换,所以之后不会因该页再次缺页。结论是 分布在 2 页、发生 2 次缺页异常,页故障地址为 00422000H、00423000H。
(2)数据访问的时间局部性
不考虑 i、j 时,程序对每个数组元素只执行一次写入,没有在短时间内反复访问同一个数组元素,因此数组元素的访问没有时间局部性。
原循环先沿一行连续访问,邻近元素也存放在邻近地址,因而具有很好的空间局部性。一个 Cache 块包含多个元素,其后续命中来源于相邻元素位于同块,不应据此把每个元素都说成被重复访问。
(3)块内地址、组号及指定元素地址
主存块长 32 B=25 B,因此低 5 位 A4~A0 为块内地址。
Cache 数据区为 8 KB,共有
328192=256
个 Cache 行,4 路组相联意味着每组 4 行,故组数为
4256=64=26.
组号占紧接块内地址的 6 位,即 A10~A5。
页大小为 212 B,虚拟地址的 A11~A0 是页内偏移,在虚实地址转换时保持不变。块内地址和组号全部位于该偏移中,所以可以直接由本题虚拟地址计算,不需要假定两个页面的物理页框连续。
a[1][0] 位于首地址之后一行,即
00422000H+256=00422000H+100H=00422100H.
该地址的组号为
⌊3200422100H⌋mod64=8,
也可取其 A10~A5 得到二进制 001000。
(4)主存块数量与两种遍历的命中率
数组首地址按块对齐,每块可容纳 32/4=8 个元素,数组共占
6144/32=192
个主存块。本题采用写分配约定:首次写缺失时先把所在整块调入 Cache,再完成写入。 题面没有单列写缺失策略,以下按这套考试计算模型,并按题设排除与其他数据的 Cache 冲突。若采用非写分配,写缺失本身不会把块调入 Cache,不能直接沿用以下命中率。写分配与写回/直写是两类不同的策略:这里需要的约定是写缺失时是否装块。
原来按行遍历时,每块的第一个元素访问缺失,随后 7 个元素命中。总访问 1536 次,缺失 192 次,命中 1536−192=1344 次,命中率为
15361344=87=87.5%.
改为按列遍历时,相邻两次访问跨过一行,即跨 256 B=8 个块,但这并不自动意味着每次都缺失。必须检查组号和路数是否足以保留先前装入的块。
首地址的 Cache 组号为 0。令 q=⌊j/8⌋,元素 a[i][j] 所在的相对块号和组号为
b(i,j)=8i+q,g(i,j)=(8i+q)mod64.
固定一列 j 时,只访问 q,q+8,…,q+56 这 8 个组,每组恰好放 3 个不同块。例如 j=0 时:
| 组号 |
映射到该组的行号 |
本轮装入的块数 |
| 0 |
0、8、16 |
3 |
| 8 |
1、9、17 |
3 |
| 16 |
2、10、18 |
3 |
| 24 |
3、11、19 |
3 |
| 32 |
4、12、20 |
3 |
| 40 |
5、13、21 |
3 |
| 48 |
6、14、22 |
3 |
| 56 |
7、15、23 |
3 |
每组有 4 路,3 个块能够全部保留。j=0 第一次访问各行的块时缺失;j=1~7 仍访问这些块,全部命中。j=8 开始各行的下一块,使用组号 1、9、……、57,随后 j=9~15 命中。每连续 8 列重复一次这个规律。
整个数组的 192 块在 64 组中各占 3 块,不超过每组 4 路,故没有由数组自身造成的替换冲突。按列遍历同样只有 192 次首次缺失和 1344 次命中,命中率仍为 87.5%。