这个图展示了在缓存中访问二维数组的两种不同方式对性能的影响。通过 sum_array_rows
和 sum_array_cols
两种函数,展示了按行访问和按列访问的区别。
缓存的假设配置
- 每个缓存组包含一个块(One block per set)。
- 每个块可以存储 8 个
double
类型的数据(即 64 字节,因为一个double
占用 8 字节)。 - 初始状态为冷缓存(cold cache),即缓存是空的。
函数分析
-
sum_array_rows
函数(按行访问):int sum_array_rows(double a[16][16]) { int i, j; double sum = 0; for (i = 0; i < 16; i++) for (j = 0; j < 16; j++) sum += a[i][j]; return sum; }
-
访问模式:按行访问,即
a[i][j]
,对于每个i
,j
从 0 到 15 遍历。这意味着程序会连续访问数组的一行内的元素。 -
缓存效果:由于每个块可以存储 8 个
double
,当访问a[i][0]
时,a[i][0]
到a[i][7]
会被加载到同一个缓存块中。- 当访问到
a[i][1]
到a[i][7]
时,数据已经在缓存中,命中率较高。 - 然后访问
a[i][8]
时,会加载a[i][8]
到a[i][15]
,这段访问也会命中。
- 当访问到
- 结果:按行访问时,缓存命中率较高,效率较高。
-
访问模式:按行访问,即
-
sum_array_cols
函数(按列访问):int sum_array_cols(double a[16][16]) { int i, j; double sum = 0; for (j = 0; j < 16; j++) for (i = 0; i < 16; i++) sum += a[i][j]; return sum; }
-
访问模式:按列访问,即
a[i][j]
,对于每个j
,i
从 0 到 15 遍历。这意味着程序会在一列内访问元素。 -
缓存效果:由于每次访问时跳到下一行的元素,
a[i][j]
和a[i+1][j]
不在同一个缓存块中。- 每次访问会导致缓存未命中,因为
a[i][j]
和a[i+1][j]
属于不同的块。
- 每次访问会导致缓存未命中,因为
- 结果:按列访问时,缓存命中率低,效率较差。
-
访问模式:按列访问,即
总结
-
按行访问(
sum_array_rows
)会充分利用缓存,使连续的数据加载到同一个缓存块中,从而提高缓存命中率。 -
按列访问(
sum_array_cols
)导致缓存不命中频繁发生,因为每次访问跳到不同的块,无法充分利用缓存。 - 因此,二维数组的访问方式对缓存命中率有显著影响。通常情况下,按行访问效率更高,特别是在直接映射缓存或低相联度缓存中。