有效地利用内存层次结构是设计现代体系结构算法的关键任务。通过平铺(有时也称为砖砌)来确保多维数组以“良好”的方式排列数据。传统的二维数组存储为一维数组,并具有索引机制;例如,一个Nx × Ny数组存储在长度为NxNy的一维数组中,二维索引(x, y)(从(0,0)到(Nx - 1, Ny- 1))使用公式映射到一维索引(从0到NxNy- 1)
$$ index=x+N_xy $$
图12.42显示了内存布局的示例。这种布局的一个问题是,尽管在同一行中的两个相邻数组元素在内存中彼此相邻,但在内存中,同一列中的两个相邻元素将被N个元素分隔开。对于较大的n,这可能导致较差的内存局部性。对此的标准解决方案是使用tile使行和列的内存局部性更加相等。如图12.43所示,其中使用2 × 2瓦片。下一节将讨论为这样一个数组建立索引的细节。下面是一个更复杂的例子,在一个3D数组上有两层平铺。
关键问题是瓷砖的尺寸。实际上,它们应该与机器上的内存单元大小相似。例如,如果我们在一台有128字节缓存线的机器上使用16位(2字节)数据值,8 × 8块正好适合缓存线。然而,使用32位浮点数,将32个元素放入缓存行,5 × 5瓦片有点太小,6 × 6瓦片有点太大。因为也有比较粗的内存单元,比如页面,所以使用类似逻辑的分层平铺是很有用的。
如果我们假设$N_x$X$N_y$的二维数组被分解成面积为n*n的铺砖(如图12.44),则铺砖的数目为:
$$ B_x=N_x/n $$
$$ B_y=N_y/n $$
这里,我们假设n能整除Nx和Ny。如果不是这样,则应该填充数组。例如,如果Nx = 15, n = 4,则Nx应更改为16。为了计算出索引这样一个数组的公式,我们首先找到铺砖索引(bx, by),为铺砖提供行/列值(铺砖本身形成一个2D数组):
$$ index=n^2(B_xb_y+b_x) $$
铺砖内的内存如图12.43所示,铺砖内的偏移$(x^{'},y^{'})$为:
因此,铺砖内的偏移offset值为:
因此,在具有n*n面积的铺砖的Nx X Ny的数组中的(x,y)元素对应的1D元素索引为:
此表达式包含许多整数乘、除和模运算,这些运算在某些处理器上代价很高。当n是2的幂时,这些操作可以转换为位移位和按位逻辑操作。然而,如上所述,理想的大小并不总是2的幂。一些乘法运算可以转换为移位/加法运算,但是除法和模运算问题更大。指数可以增量计算,但这将需要跟踪计数器,有大量的比较和较差的分支预测性能。
然而,有一个简单的解决方案;注意,索引表达式可以写成: