Roofline Model Toolkit: A Practical Tool for Architectural and Program Analysis

Roofline Model Toolkit: A Practical Tool for Architectural and Program Analysis 描述了 Roofline Toolkit 的原型架构表征引擎。该引擎由一组使用消息传递接口(Message Passing Interface,MPI )以及用于表示线程级并行性的 OpenMP 实现的便携式设备化微基准组成,可量化多核、众核和加速系统的带宽和计算特性。这些微观测试侧重于在编译器和运行时环境以及线程级并行、指令级并行和显式 SIMD 并行中捕获内存层次结构的每个级别的性能。

文章构建了三个基准测试:前两个展现 Roofline 的常规内存层次带宽和浮点计算特性。第三个基准测试用于分析局部性对 GPU 等加速架构的重要性,其量化了 GPU 上显式和隐式管理的空间和时间局部性之间的性能关系。

为了验证自动化策略的实用性,作者评估了四种完全不同的体系结构上的性能:

  • 传统的超标量乱序 Intel Xeon 多核处理器(Edison);
  • 低功耗双发射顺序 IBM Blue Gene/Q 多核处理器(Mira);
  • 高性能顺序 Intel Xeon Phi 多核处理器(Babbage) ;
  • 高性能 NVIDIA Kepler K20x GPU 加速系统 (Titan)。

相应代码为 berkeleylab/cs-roofline-toolkit,然而其中仅包含参考 C 代码。

2 Related Work

如今,数据移动往往主导计算。通常,这种数据移动在 DRAM 和缓存层次结构之间进行,并且通常是结构化流(阵列)访问。因此,STREAM 基准测试已成为对多核处理器的极限 DRAM 带宽基准测试的事实解决方案。STREAM 是 OpenMP 线程化的,将执行一系列基准测试,这些基准测试旨在将内存子系统的性能量化为常见数组操作的函数。不幸的是,所有这些操作都写入目标数组而没有读取它。因此,写分配操作所必需的隐藏数据移动实际上阻碍了带宽。今天的指令集体系结构(ISA)通常提供一种绕过这种写分配操作的手段。遗憾的是,编译器很少会在实际应用程序中正确生成此操作。因此,我们有动机使用只读(求和或内积)或读-改-写(增量)基准来扩充流,以便清晰地量化这种隐藏的数据移动。

现代微处理器使用硬件流预取器通过推测加载缓存行来隐藏内存延迟。遗憾的是,这些预取器的性能高度依赖于体系结构,并且已经观察到带宽与连续访问的元素数量高度相关[15]。内存访问的短“节”导致性能大幅下降。创建 Stanza Triad 是为了量化这种影响 [9]。不幸的是,它没有线程化,因此无法识别在多核处理器上运行时何时从并发限制状态过渡到吞吐量限制状态。

当 DRAM 带宽不是节点上应用程序性能的瓶颈时,缓存带宽通常是。CacheBenchLLCbench 的一部分)可以用来理解缓存层次结构的容量和带宽。不幸的是,CacheBench 没有用 OpenMP 线程化或 MPI 并行化。因此,它无法在缓存层次结构的任何级别(包括像 STREAM 这样的 DRAM)上测量争用。与其采用这种纯粹的经验方法,不如在有足够文档的情况下,使用 Execution Cache Memory 模型创建缓存层次结构的分析模型。

也许与我们最相似的工作封装在用于驱动 Energy Roofline Model 的基准中。在这项工作中,构建了一系列不同算术强度的实验,以了解架构在性能和功耗方面的响应。当与缓存基准相结合时,可以推断出各种计算和数据移动操作的能量需求。他们的目标主要集中在功率和能源上,而我们则专注于性能。

3 Experimental Setup

3.1 Architectural Platforms

表 1 总结了这些平台的关键架构特征。请注意,所示峰值 GFlop/s 和带宽是理论值。
table1
Edison:即 Cray XC30,是 NERSC 的 MPP。每个节点包括两个标称主频为2.4GHz 的12核 Xeon E5 2695-V2处理器(TurboBoost 可以大幅提高这一频率)。每个核都是超标量、乱序的2路超线程核心,除了加载和存储外,每个周期还能够执行两条 4 路 AVX SIMD 指令(加法和乘法)。每个核心都有一个私有的32KB L1数据缓存和一个私有的256KB L2缓存。芯片上的12个核心共享一个30MB 三级缓存和一个连接到四个 DDR3-1600 DIMM 的内存控制器。大量的流预取器旨在使缓存层次结构的每个级别的带宽饱和。从理论上讲,该处理器的超标量和无序特性应该会减少对优化软件和编译器优化的需求。

Mira:是安装在阿贡国家实验室的 IBM Blue Gene/Q 系统。每个节点包括一个16核 BGQ SOC。16个 A2核中的每一个都是4路 SMT 双发射有序核心,每个周期能够执行一条 ALU/加载/存储指令和一条四路 FMA。然而,为了达到这个吞吐率,每个核心必须至少运行两个线程。每个核心都有一个私有的16KB 数据缓存,16个核心共享一个32MB 的 L2缓存,通过纵横开关连接。理想情况下,这种体系结构的 SMT 特性应该隐藏大量指令和缓存延迟的大部分影响。然而,当整数指令在动态指令组合中占很大比例时,处理器的双发射特性可能会影响性能。

Babbage:是 NERSC 的 Knights Corner (KNC) 众核集成核心 (MIC) 测试平台。KNC 处理器包括 60 个双发有序4路超线程核心。每个核心包括一个32KB L1数据缓存、一个512KB L2缓存和一个8路向量单元。虽然 L2缓存是一致的,但是环形 NoC 拓扑加上一致性机制可能会影响性能。与前述多核处理器不同,这款众核处理器使用超高速 GDDR 内存,提供超过 350GB/s 的理论引脚带宽。为了代理未来将构成 NERSC8 超级计算机 Cori 核心的 Knights Landing (KNL) MIC 处理器,我们以“本地”模式进行所有实验。因此,不运用主机处理器、主机内存和 PCIe 连接。

Titan:是橡树岭国家实验室的 Cray 加速 MPP 系统。每个节点包括一个16核 AMD Interlagos CPU 处理器和一个 NVIDIA K20x GPU。每个 GPU 包括14个流式多处理器(SMX),每个处理器可以调度256个32线程束,并一次向其192个 CUDA 内核发射4个指令。每个 SMX 有一个256KB 的寄存器文件、一个64KB 的 SRAM,其可以划分为 L1缓存和共享内存(暂存器)段。每颗芯片都包含一个所有 SMX 共享的1.5MB L2缓存,并连接到引脚带宽为 232GB/s 的高速 GDDR5内存。不幸的是,Titan 生产系统上的软件往往落后于 NVIDIA 版本。因此,我们在 NERSC[10]的 Dirac 测试台中使用了类似的 K20xm 来评估 CUDA 统一虚拟地址和统一(托管)内存。 就我们的目的而言,K20x 和 K20xm GPU 是相同的。

4 Memory and Cache Bandwidth

今天,带宽和数据移动也许是科学应用性能的首要方面。遗憾的是,正如相关工作中所讨论的,大多数现有的基准无法代表与实际应用程序相关的争用、局部性或执行环境。为了解决这个问题,我们创建了一个使用混合 MPI+OpenMP 模型的 Roofline 带宽基准测试。因此,希望代理平面 MPI 代码的程序员在平面 MPI 模型中运行 Roofline 基准测试。 那些希望了解 NUMA 架构性能的人可以在混合模式下运行。

4.1 Bandwidth Code

CacheBench 一样,我们的 Roofline 带宽基准测试旨在使用简单的单位步长流式内存访问模式量化内存层次结构中每个级别的可用带宽。然而,与 CacheBench 不同的是,它包括由线程并行和有限 NoC 带宽引起的争用效应。在该机制中,它类似于 STREAM 代码,后者使用 OpenMP 工作共享结构在多个线程之间拆分循环迭代(图 1)。我们的 Roofline 带宽代码没有使用工作共享结构,而是创建了一个并行区域,并将线程静态分配到数组索引范围。所有初始化、同步和计算都在此并行区域内进行。计算表示为有限几何级数的总和,因为希望没有编译器可以自动消除此嵌套循环。 本质上,几何级数中的每一项都是 STREAM 基准测试中的一次试验。

在这里插入图片描述

因此,基准可以用于量化内存层次结构的每个级别的容量以及级别之间的带宽。此外,通过调整参数,可以估计 MPI 或 OpenMP 屏障的开销。由于基准是 MPI+OpenMP,因此可以在所有规模上探索这些带宽和开销。

4.2 Bandwidth Result

图 2 显示了在四个平台上运行的 Roofline 带宽基准测试的结果。在 Edison 上,我们每个节点运行两个进程,而其他所有机器以单个进程运行。 请注意,x 轴表示所有线程的总工作集。蓝线标记了内存层次结构每个级别的理论带宽和容量。在 CPU 架构上,红线表示生成的 Roofline 带宽。
在这里插入图片描述
Edison:在 Edison 上,硬件非常接近理论性能,并且在预期的缓存容量下发生了转换。缓存容量带宽的平滑过渡表明缓存替换策略可能不是真正的 LRU 或 FIFO,而是伪变体(Pseudo-LRU)。值得注意的例外是 Edison 未能接近 DRAM 引脚带宽。这并不一定令人意外,因为很少有机器具有如此高的带宽,而且很少有机器能够达到引脚带宽。此外,简单的读-修改-写内存访问模式对于这种架构来说可能是次优的。未来的工作将探索改变读取和写入之间平衡的替代内核。

Mira:在 Mira 上,性能始终低于理论带宽限制,并且这些转换似乎表明有效缓存容量降低。低 L1带宽尤其令人惊讶,可能表明存在直写或直通存储 L1 架构。需要进一步调查。

Babbage:在高度多线程的 MIC(Babbage)上,我们发现为了获得良好的性能,需要对超过1MB(每个线程超过 4KB)的工作集进行操作。由于该架构每个周期可以加载64个字节,因此认为需要64个加载来分摊基准测试中的任何循环开销并非不合理。对于较小的工作集,性能下降,表明资源利用率不足。总的来说,该基准正确地识别了 L1和 L2缓存容量,但获得的带宽远小于理论值。低 L2带宽可归因于缺乏像 Edison 和 Mira 那样的 L2流预取器。如果编译器未能理想地插入软件预取,则会暴露内存延迟。相反,低 DRAM 带宽是这台机器上的一个已知问题,需要硬件解决方案来纠正。

Titan:在 Titan 上,使用 GPU,我们发现运行三个略有不同的内核能够直观地量化 GPU 内存层次结构中显式和隐式重用的影响。内核 A(图 2 (d) 上的“global tInside”图例)和内核 B(“global tOnside”)都使用全局内存,但分别在内部和外部进行试验循环。 内核 C(“sharemem”)将全局内存数据复制到共享内存,在内核内部执行试验循环,然后复制回全局内存。

“Kernel B”可能与 CPU 实现最为相似。整个工作集跨线程块并行化,求和(重用)发生在 CUDA 内核级别。也就是说,几何和的每次迭代都有一个内核调用。我们将性能作为线程块大小(32或64)的函数进行探讨,并使用恒定的224个线程块。与 Babbage 一样,我们看到,在小的工作集规模下,CUDA 内核开销过大导致了利用率严重不足,但性能最终在 DRAM 限制下饱和,尽管这远低于理论引脚带宽。“Kernel A”重构求和循环以增加线程块内的局部性,并以此为基础,为每个线程块工作集(注意,总共有 7168 或 14336 个线程)运用 L1 缓存我们发现在小规模(较少的 CUDA 内核调用)下的性能要好得多,并且在达到 DRAM 限制之前,性能可以达到 L1和 L2极限。最后,“Kernel C”再次重构循环并以阻塞方式利用共享内存。因此,它可以达到共享内存约 1.3TB/s 的理论性能极限。

总体而言,众核和加速器的带宽性能趋势有点令人不安。也就是说,获得高性能的唯一方法是在大型工作集上实现大规模并行。对于实际应用,这种观察将使借助加速器或众核处理器更快地解决现有问题变得困难。相反,人们将能够在可比的时间内解决更大的问题。尽管如此,这个基准可以用来指导程序员何时能够迁移到多核或加速架构。

5 Floating-Point Compute Capability

尽管很多应用受到内存带宽的限制,但仍有一些应用受限于片上计算并最终受限于核心性能。当性能处于临界点时,适当地利用指令级、数据级和线程级的并行性可以确保代码不受人为限制。遗憾的是,在现代众核和加速架构中,准确衡量这些并行方面重要性的基准相对较少。为了解决这个不足,我们构造了一个 Roofline 浮点基准。

5.1 Reference Roofline Floating-point Benchmark

我们修改了 Roofline 带宽基准,为每个元素实现一个多项式。通过改变多项式的阶数(预处理器宏),可以改变每个元素的 FLOPs。这样做可以将加载(存储)和浮点运算之间的平衡从 L1 限制更改为 FLOP 限制。图 3 展示了该基准测试的一个示例。
fig3

可以看出,这个例程中每个线程的并行度是 O ( n s i z e ) O(\mathrm{nsize}) O(nsize)。有序处理器将提供受浮点延迟而不是峰值性能限制的性能。编译器可以展开此循环(至少通过浮点延迟)并表达指令级并行性和(或) SIMD 化展开的代码以利用数据级并行性。或者,具有足够深的重新排序缓冲区的无序处理器可以找到内在的指令级并行性,从而获得高性能。尽管无序并行可以对指令流进行重新排序,但它永远无法自动对指令流进行 SIMD。因此,如果没有编译器对 SIMD 的支持,它永远无法达到最佳性能。

5.2 Performance as a Function of Implicit and Explicit Parallelism

在当今的处理器上,线程级和数据级并行性在编译器生成的代码中必须是显式的。由于自动并行化和自动向量化编译器很少是绝对可靠的,因此这些形式的并行性通常也必须在源代码中明确显示。为了量化体系结构在编译代码上可以获得的性能与体系结构的真实性能之间的差异,我们实现了三个显式展开和 SIMD 化(通过内蕴函数)的 Roofline 浮点基准:AVX、QPX 和 AVX-512版。作为线程级并行性和展开(显式指令级并行性)的函数,图 4 展示了这些实现在 Edison、Mira 和 Babbage 上的性能。请注意,每个实现使用不同数量的单位元素 flops (FPE)。
fig4

Edison:Edison 用编译的 C 代码实现的峰值略低于宣称峰值的一半。但是,当使用优化的实现时,性能显着提高,实际上可以超过460 GFlop/s 的标称峰值性能。比光速快的效果是由于在这台机器上启用了 TurboBoost。12核最高频率2.8GHz,其真实峰值性能约为537 GFlop/s——非常接近观察到的性能。为了验证这一点,我们使用aprun --p-state选项将频率固定在宣传的2.4GHz 处,性能与预期一致。尽管机器对指令级并行(展开)很敏感,但通常不需要超线程来获得令人满意的性能。

Mira:在 Mira (BGQ) 上运行一组类似的实验,我们看到了截然不同的结果。首先,编译后的代码提供了非常好的性能。这表明 XL/C 编译器能够有效地 SIMD 化并充分展开代码以隐藏浮点延迟。通过使用显式展开的代码,我们观察到需要大量展开(每个线程 2-4 条 SIMD 指令)才能达到峰值性能。与 Edison 不同,Mira 显然需要两个线程才能达到峰值性能。

Babbage:Babbage 呈现出类似于 Edison 和 Mira 混合的特征。即使在这个简单的内核上,编译器显然也未能充分利用体系结构。随着充分的展开(每线程4个 SIMD 指令),性能在两个线程后开始饱和。只有极高的强度(每个元素256个 flop)才使性能接近峰值。

5.3 Performance as a function of L1 Arithmetic Intensity

即使可以在 L1 中维护工作集,性能也将取决于动态指令组合和核心的发射能力。在本节中,我们利用 Roofline 浮点基准将性能量化为 L1 算术强度的函数,表示为单位元素的 Flops(FPE)——本质上是多项式的次数。对于每种架构,我们都运行了量化架构能力的参考 C 代码以及性能最佳的 SIMD 化和展开实现。图 5 显示了每种架构的组合性能。作为参考,我们包含一个微体系结构性能模型(蓝色),该模型考虑了在内核要求的混合情况下,加载/存储相对于浮点指令的发射速率。
fig5

Edison:图 5 表明 Edison 可以快速达到其峰值性能,并且该性能与理论模型十分吻合。一般来说,低 FPE 下性能会下降,因为内核可以每周期执行8个 flop,但只能支持每周期加载和存储2个元素。有趣的是,参考 C 代码的性能在高 FPE 时下降。这可能是重新排序缓冲区的一个限制,要求持续寻找5条独立浮点指令。

Mira:Mira 在编译代码和优化代码上的性能都向右偏移。通常,这表明附加指令在消耗与加载或存储相同的发射槽。在双发射 A2架构上,这很可能是整数或分支指令。这种影响在 Edison 上并不存在,因为它是一个超标准处理器,可以从浮点或加载(存储)端口以外的端口发射整数或分支指令。有了足够的 FPE,性能就可以达到峰值。

Babbage:Babbage 表现出第三种行为——逐渐接近峰值性能。这种行为表明附加指令(例如整数或分支)与浮点指令消耗相同的发射槽。因此,性能表现为 F P E = ( F P E + k ) \mathrm{FPE}=(\mathrm{FPE} + k) FPE=(FPE+k),其中 k k k 是影响性能的额外指令的数量。

Titan:最后,我们构建了一个类似的 CUDA C 基准测试以在 GPU 上运行。理论界限基于以下假设:每个加载(存储)单元支持从内存中每个周期加载 4 个字节(每个 SMX 128 个字节)。我们观察到 GPU 的性能似乎同时体现了 BGQ 和 MIC 的特性。也就是说,没有足够的分发带宽来完全驱动内核,SMX 无法支持每个周期从内存加载(存储)128字节。

6 Beyond the Roofline — CUDA’s Unified Memory

迄今为止,加速架构通常被用作一个加速器,专用内存通过 PCIe 或类似总线连接到常规系统上。这种专用内存不仅拥有独立的地址空间,而且程序员必须通过库接口显式地向设备拷入拷出数据。这样做不仅低效,而且暴露了 PCIe 带宽和设备带宽之间的性能差距。

最近,CUDA 引入了两个内存概念——Unified Virtual Address(UVA)空间和 Unified Memory(即托管内存)。顾名思义,UVA 统一了 CPU 和 GPU 地址空间,并确保(在程序层面)程序可以透明地加载和存储内存,而不必担心数据的位置(为了正确性)。由于数据仍然固定在主机或设备上,因此存在强烈的 NUMA 效应。统一(托管)内存通过在主机和设备之间迁移数据来扩展此过程。因此,可以将设备内存视为 CPU 内存上的缓存。理想情况下,这将解决许多生产力和性能挑战。在本节中,我们将这些方法的性能作为空间和时间局部性的函数进行评估。

6.1 CUDA Managed Memory Benchmark

我们测试 CUDA 内存最初方法的是创建一个在主机和设备之间来回传输数据的基准测试。为此,我们通过让 GPU 执行 k − 1 k − 1 k1 次求和迭代并让 CPU 执行 1 1 1 次来重用 Roofline 带宽基准。随着净复用 k k k 的增加,我们预计将摊销在主机和设备之间移动数据的成本。

请注意,这个基准在实际中并不是一个不合理的场景,因为很多应用可能会将一些数据打包给GPU,复制到设备上,在其上运行几次,然后返回给主机。如果使用统一内存写入,数据将在主机和设备之间来回摆动。

在本文中,我们使用四种不同的方法来控制设备上数据的局部性来评估性能。

  • 首先,我们使用分配在主机上的分页数组或页锁定数组来评估传统的显式复制 (cudaMemCpy) 方法。
  • 接下来,我们评估零拷贝内存的性能。在这种情况下,数据被分配并固定在主机上,CUDA 运行时负责将加载和存储请求映射到 PCIe 传输。
  • 最后,我们评估了统一(托管)内存结构的性能,其中 CUDA 运行时可以迁移数据。

图 6 给出了这些实现。可以看出,多次 CUDA 内核调用能够增加局部性。宏_CUDA_ZEROCPY_CUDA_UM分别选择使用页锁定主机零拷贝和统一内存管理。页锁定主机内存使用普通的malloc()函数在主机上分配内存,然后使用cudaHostRegister()在主机内存地址空间上注册设备指针。 对于统一内存,使用cudaMallocManaged来分配主机和设备内存。
fig6

6.2 Results

由于 Titan 尚不支持 CUDA 6,我们所有的实验都是在 Dirac 集群中的类似 K20xm 上运行的。图 7 显示了四种技术的最终“有效带宽”随工作集大小和临时复用的变化关系。对于较小的工作集规模,CUDA 内核启动时间占主导地位,有效带宽很低。这只是强化了不使用 GPU 进行小数据量操作的传统认知。比较图 7(a) 和 (b),我们看到仅在大型工作集重用50-100次时才有可能接近设备带宽限制。因此,如果预计需要数百次迭代才能收敛,将迭代求解器负载分流到 GPU 是一个可行的选择。相反,对于复用最少的大型工作集,我们看到锁页内存提供了更好的 PCIe 带宽。
在这里插入图片描述

由于 Zero Copy 内存没有提供缓存效益,我们在图 7(c) 中没有看到局部性增加带来的性能优势。相反,图 7(d) 展示了使用统一内存自动管理设备上数据局部性所带来的性能收益。从广义上讲,性能与显式管理局部性的性能在性质上是相似的。不幸的是,原始性能要低得多。对于能够保证设备上1000路复用的应用程序,统一内存将提供高效且高性能的解决方案。人们只能希望硬件和运行时的进步可以弥合较低时间局部性的性能差距。

未来的工作将扩展这项技术以跟踪 NVIDIA 实现的任何软件缓存一致性协议的发展。也就是说,没有理由 CPU 和 GPU 都必须读取-修改-写入数组。任何一个都可以执行只读操作。

7 Empirical Roofline Models and Their Use

现在,我们已经对四个平台的带宽和计算特性进行了基准测试,我们可以为每个平台构建经验 Roofline 模型。图 8 显示了每个平台使用 DRAM 和 L1 带宽以及理论或“教科书”Roofline 的结果模型。理想的架构是能够充分利用其构建技术的架构。我们看到,总的来说,Edison 的经验性能非常接近其理论极限。相反,在 Mira 和 Babbage 上,我们看到理论与现实之间存在巨大差异。极端的多线程范式允许 GPU 在设备上运行时提供较高的理论带宽占比。
在这里插入图片描述

参考资料:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值