• 沒有找到結果。

存储墙问题一直是计算机系统发展的瓶颈。 现代处理器速度的快速发展和存储器速度的慢 速发展导致处理器要花费大量的时间等待存储器数据的返回, 这就是存储墙问题。 在过去的十 几年中, 处理器速度以每年 50%~100%的速度平稳增长, 而存储器的速度却只以每年 7%左右 的速度增长。 我们可预计处理器与存储器之间的速度差异将会越来越大, 所以存储系统仍将是 影响整个计算机系统性能的一个关键瓶颈。在并行计算体系结构中,计算速度更快,存储墙问 题更加突出,因此,MIC  众核处理器需要开启数百个线程,存储器的访问优化对性能的影响 极为突出。 

9.2.4.1  MIC 层次存储结构 

MIC 卡不仅核架构与 CPU 类似——基于 x86 架构,存储器结构也和 CPU 类似。MIC 采 用了两级 Cache 结构,KNC 芯片框图如图 9­7 所示,MIC 层次存储结构如图 9­8 所示。 

KNC 卡包含 8 个双通道 GDDR5 内存控制器,内存传输带宽为 5.5GT/s。 

KNC 包含两级 Cache 结构:L1 Cache 和 L2 Cache。 

KNC 每个核包含 32KB L1 指令 Cache 和 32KB L1 数据 Cache,L1 Cache line 为 64B,采 用 8 路关联,8 个 bank,L1 Cache 为每个核私有,访问速度快。 

KNC 拥有共享的 L2 Cache,每个核上的 L2 Cache 包括 L1 的数据缓存和指令缓存。不仔 细分析可能不清楚那些核之间是怎么组织成一个大的共享的 L2 Cache(达到 31MB)。因为每 一个核包含 512  KB  L2  Cache,62 个核的 L2  Cache 即为 31MB,看起来好像 31MB  L2  Cache  都是可用的。然而,如果两核或多核之间共享数据,这些共享数据在不同核的 L2  Cache 中是 重复的。如果核之间没有共享任何数据或代码,那么片上 L2 的全部的大小为 31MB,相反如

9

Chapter

果每一个核同时共享相同的代码或数据,那么片上 L2 的全部的大小仅仅为 512KB(每个核上  L2 Cache 存放相同的 512KB 数据) 。

图 9­7  KNC 芯片框图

图 9­8  MIC 层次存储结构 

9.2.4.2  MIC 存储器访问优化策略

在 MIC 平台上提高存储器访问性能主要通过两种方法: 

1.隐藏存储器访问延迟

隐藏存储器访问延迟的基本思想是在处理器进行计算时, 如果出现访问存储器时发生延迟,

则可以通过预先的存储器操作或者另外的计算将这些延迟和处理器的计算重叠起来,使处理器 不至于因为等待存储器操作的结果而停顿。下面给出 MIC 中两种常用的隐藏访存延迟的方法:

(1)多线程:多线程基于暴露线程级并行的思想来隐藏访存延迟。比较典型的就是同时 多线程技术, 其基本思想是在一个线程的指令发生访存延迟的时候, 从另一个线程中选择适当 

MIC core  L1 cache  L2 cache

9 Chapter

的指令执行,这样不至于让处理器发生停顿。MIC 卡每个 core 支持最多 4 个线程,采用了硬 件多线程技术,该技术就是通过多线程隐藏访存延迟,因此,在 MIC 程序设计时要尽量提高 并行度,使每个核上运行 3~4 个线程比较理想。

(2)预取:预取技术指在处理器需要数据或者指令之前将其从存储器中取出,以备需要 时使用。目前的预取技术可以分为硬件预取(扩展存储器管理子系统的体系结构)、软件预取

(利用现代处理器的非阻塞预取指令)和混合预取三种。MIC 支持硬件预取,由 MIC 硬件自 动完成。 

2.利用 Cache 优化

通常情况,程序访存时绝大部分时间集中在少量的区域,大量实验表明,程序执行时 90% 

的访存集中在  10%的区域,这就是程序的局部性原理。程序的局部性又可以分为两种,一种 是时间局部性(Temporal Locality) ,指某一区域如果被访问,那么它很快被再次访问的几率较 大;另一种是空间局部性(Spatial Locality) ,指某一区域如果被访问,那么它相邻的区域很快 被访问的几率较大。 

MIC 包含 L1 和 L2 两级 Cache,充分利用程序的局部性原理,可以提高 Cache 命中率,

也就可以提高访存效率,下面小节详细介绍 MIC 上 Cache 优化方法。 

9.2.4.3  Cache 优化方法 

Cache 优化主要利用程序的局部性原理,代码级别的 Cache 优化主要有两个方法: 

9 注意的是“尾巴”的处理,如下面示例中:for(i=it; i<min(it+nb, n); i++)。

程序循环分块示例: 

9 Chapter

通过代码变换来提高 Cache 性能的基本思想是改变指令执行的顺序, 从而改变 Cache 命中 率。 基于循环的代码变换能否有效地提高 Cache 性能取决于循环访问数据的局部性特性。 这样 每种代码变换都需要基于其访问的数据建立相应的代价模型, 这是代码变换的一个难点。 在进 行代码变换的时候, 不但需要确保变换后程序的正确性, 而且还需要确保变换前后程序可执行 语义的等价性,这些都是代码变换的难点。代码变换也有一些优点,例如代码变换通过改变程 序自身的局部性(包括时间局部性和空间局部性)来提高 Cache 性能,与平台无关,无须特定 硬件的支持,也就是说 MIC 版本的升级无需重新设计代码变换。 

2.数据变换

相比于代码变换改变程序指令的执行顺序, 数据变换主要是改变程序中数据的布局, 依据 空间局部性原理提高数据 Cache 性能。数据变换的基本思想是:当程序访问数据时,将经常一 起访问的数据组织在一起,使其在内存中的位置邻近。这样当发生 Cache 失效的时候,每次调 入相应的 Cache 块, 紧接着访问的数据也就由于在同一 Cache 块而被一起调入, 在一定程度上 减少了 Cache 失效次数。下面给出了两种常见的数据变换方法:

(1)数据放置

程序变量的地址在编译时或者运行时才能决定,这些地址决定了程序数据在内存中的位 置,也就决定了这些数据在 Cache 中的位置。例如在虚拟索引的 Cache 中,变量的地址与数据  Cache 大小取模后就能得到变量所在 Cache 的相应地址。因此,可以通过将变量放置在适当的 位置来决定其对应的  Cache  的位置。如果将经常一起访问的变量通过重新放置使其位于同一  Cache 块上,则能有效提高数据的空间局部性。在实际的数据放置实现中,需要先判断数据之 间的关系来选择放置在一起的数据,常见的方法有 Clustering 和 Coloring 等,这种方法对一些 基于指针的数据结构效果特别明显。

(2)数组重组

在科学计算程序中, 大量的数据是以数组的形式出现, 对这些数组进行重组能够有效地提 高 Cache 性能。 在这样的程序中经常出现一些循环, 这些循环以循环变量为下标访问多个数组,

循环的每次迭代都需要到各个数组所在的内存区域读取数据, 如果将这些数组重组成以结构体 为元素的数组, 则循环的每次迭代用到的数据都在一个结构体中, 读取这些数据时就只需访问 内存中一个连续的区域,提高了数据的空间局部性,相应的 Cache 失效次数就会减少。

相關文件