正如我们在第5.1节中解释的那样,DRAM突发是一种并行组织形式:在DRAM核心阵列中并行访问周围的多个位置。然而,仅靠bursting不足以实现现代处理器所需的DRAM访问带宽水平。DRAM系统通常采用另外两种形式的并行组织——banks and channels。在最高级别,处理器包含一个或多个通道。每个通道都是一个带有总线的内存控制器,该总线将一组DRAM组连接到处理器。图5.7说明了一个包含四个通道的处理器,每个通道都有一个将四个DRAM组连接到处理器的总线。在实际系统中,处理器通常有一到八个通道,每个通道都连接到大量bank。
总线的数据传输带宽由其宽度和时钟频率定义。**现代双数据速率(DDR)总线每个时钟周期执行两个数据传输器,一个在上升边缘,一个在每个时钟周期的下降边缘。**例如,时钟频率为1 GHz的64位DDR总线的带宽为8B21 GHz=16GB/秒。这个数字似乎很大,但对于现代CPU和GPU来说往往太小了。现代CPU可能需要至少32 GB/S的内存带宽,而现代GPU可能需要128 GB/s。在这个例子中,CPU需要2个通道,GPU需要8个通道。
对于每个通道,连接到它的bank数量由充分利用总线数据传输带宽所需的 bank 数量决定。图5.8中说明了这一点。每个库包含一个DRAM单元阵列,用于访问这些单元的传感放大器,以及向总线传输突发数据的接口(第5.1节)。
图5.8(A)说明了单个bank连接到通道时的数据传输时间。它显示了对bank中DRAM单元的两个连续内存读取访问的时间。回顾第5.1节,每次访问都涉及很长的延迟,解码器使电池能够与传感放大器共享其存储的电荷。此延迟显示为时间框架左端的浅灰色部分。一旦传感放大器完成工作,突发数据就会通过总线传输。通过总线传输突发数据的时间显示为图5.8(A中时间框架的左侧黑暗部分。。在传输突发数据(右黑暗部分)之前,第二个内存读取访问将产生类似的长访问延迟(时间框架黑暗部分之间的光部分)。
实际上,访问延迟(光部分)比数据传输时间(暗部分)长得多。很明显,单bank组织的访问传输时间将严重不足利用通道总线的数据传输带宽。例如,如果DRAM单元阵列访问延迟与数据传输时间的比率为20:1,则通道总线的最大利用率为1/21 = 4.8%。这是一个16GB/s的通道,将以不超过0.76 GB/s的速度向处理器传输数据。这是完全不能接受的。通过将多个bank连接到通道总线,可以解决这个问题。
当两家bank连接到一个通道总线时,可以在第二家bank启动访问,而第一家bank正在提供另一个访问。因此,可以重叠访问DRAM单元阵列的延迟。图5.8(B)显示了两家bank组织的时机。我们假设 bank 0 的开始时间早于图 5.8(B)中显示的窗口。在第一家 bank 开始访问其cell array后不久,第二个 bank 也开始访问其单元格数组。当 0 bank 的访问完成时,它会传输突发数据(时间范围最左边的黑暗部分)。一旦bank 0完成数据传输,bank 1可以传输其突发数据(第二个黑暗部分)。这种模式在下一次访问中重复。
来自图 5.8(B),我们可以看到,通过拥有两个 bank,我们可以将通道总线的数据传输带宽的利用率增加一倍。一般来说,如果单元阵列访问延迟和数据传输时间的比率是R,如果我们希望充分利用信道总线的数据传输带宽,我们至少需要有R+1 bank。例如,如果比率为20,我们需要至少21个 bank 连接到每个通道总线。实际上,连接到每个通道总线的 bank 数量需要大于R,原因有二。一个是拥有更多 bank 可以降低针对同一 bank 同时进行多次访问的概率,这种现象被称为 bank conflict。由于每个 bank 一次只能提供一个访问,因此对于这些冲突的访问,单元阵列访问延迟不再重叠。拥有更多的 bank 会增加这些访问权限分散在多家 bank 的可能性。第二个原因是,每个单元阵列的大小都设置为实现合理的延迟和可制造性。这限制了每家bank可以提供的单元格数量。人们可能需要大量的bank 才能支持所需的内存大小。
线程的并行执行与DRAM系统的并行组织之间存在重要的联系。为了实现为设备指定的内存访问带宽,必须有足够数量的线程可以同时进行内存访问。此外,这些内存访问必须均匀地分配给通道和bank。当然,正如我们在第5.1节中研究的那样,对 bank 的每次访问也必须是合并的访问。
图5.9显示了向通道和 bank 分配数组M元素的玩具示例。我们假设两个元素(八字节)的小突发大小。分发是通过硬件设计完成的。通道和bank的地址是,阵列的前八字节(M[0]和M[1])存储在通道0的bank0中,接下来的八字节(M[2]和M[3])存储在通道1的bank0中,接下来的八字节(M[4]和M[5])存储在通道2的bank0中,接下来的八字节(M[6]和M[7])存储在通道3的bank0中。
此时,分布wraps回通道0,但将在接下来的八个字节(M[8]和M[9])中使用bank1。这样,元素M[10]和M[11]将位于第1频道的1bank,M[12]和M[13]将位于第2频道的第1bank,M[14]和M[15]将位于第3频道的第1bank。虽然图中没有显示,但任何额外的元素将被包裹起来,并从通道0的0组开始。例如,如果有更多的元素,M[16]和M[17]将存储在通道0的0bank中,M[18]和M[19]将存储在通道1的0 bank中,等等。
图5.9中所示的分配方案,通常被称为交错数据分发,将元素分散到系统中的bank和渠道。该方案确保即使是相对较小的阵列也能很好地分布。也就是说,在进入通道1的0bank之前,我们只分配足够的元素来充分利用通道0的DRAM突发。在我们的玩具例子中,只要我们至少有16个元素,分配将涉及存储元素的所有渠道和bank。
我们现在说明并行线程执行和并行内存组织之间的交互。我们将使用图4.9中的例子,复制为图5.10,我们假设乘法将用 2x2 线程块和 2×2 tile 进行。
我们现在说明并行线程执行和并行内存组织之间的交互。我们将使用图4.9中的例子,复制为图5.10.。我们假设乘法将用2x2线程块和2X2 tile进行。
在内核执行的 0 阶段,所有四个线程块都将加载其前一个 tile。每个 tile 中涉及的M元素如图5.11.所示。第2行显示了在 0 阶段访问的M元素及其二维索引。第3行显示相同的M元素及其线性化指数。假设所有线程块都是并行执行的。我们看到,每个区块将进行两个合并访问。
根据图5.9中的分布,这些合并访问将进入0频道的两家bank以及2频道的两家bank。这四个访问将并行进行,以利用两个通道,并提高每个通道的数据传输带宽的利用率。
我们还看到Block0,0和Block0,1将加载相同的M元素。大多数现代设备都配备了缓存,只要这些块的执行时间足够接近,这些缓存就会将这些访问合并成一个。事实上,GPU设备中的缓存存储器主要旨在结合此类访问并减少对DRAM系统的访问次数。
第4行和第5行显示了内核执行第1阶段加载的M元素。我们看到,现在可以在通道1和通道3中访问bank。再一次,这些访问将同时进行**。读者应该清楚,线程的并行执行和DRAM系统的并行结构之间存在共生关系。**一方面,良好地利用DRAM系统的潜在访问带宽需要许多线程同时访问驻留在不同bank和通道中的数据。另一方面,设备的执行吞吐量依赖于DRAM系统并行结构的良好利用。例如,如果同时执行线程在同一通道中的所有访问数据,内存访问吞吐量和整体设备执行速度将大大降低。
请读者验证是否将两个较大的矩阵相乘,例如具有相同2×2线程块配置的8x8,将利用图5.9.中的所有四个通道。此外,增加DRAM突发大小需要乘以更大的矩阵,以充分利用所有通道的数据传输带宽。