《Optimizing the LINPACK Algorithm for Large-Scale PCIe-Based CPU-GPU Heterogeneous Systems》阅读笔记

论文标题

《Optimizing the LINPACK Algorithm for Large-Scale PCIe-Based CPU-GPU Heterogeneous Systems》

为基于 PCIe 的大规模 CPU-GPU 异构系统优化 LINPACK 算法

作者

Guangming Tan、Chaoyang Shui、Yinshan Wang、Xianzhi Yu 和 Yujin Yan

来自中科院计算所

初读

摘要

  • 在异构并行系统中,GPU与其他组件(CPU、PCIe 总线和通信网络)之间的性能差距日益扩大,这迫使我们比以往更加细致地协调这些组件之间的协同执行。
  • 本文以 LINPACK 基准测试为案例研究,提出了一种针对大规模 CPU-GPU 异构集群系统的细粒度流水线算法。
    • 首先,我们构建了一个算法模型,揭示了以 GPU 为中心和细粒度流水线算法设计的新方法。
    • 接着,我们展示了四种模型驱动的流水线算法,它们逐步消除流水线中的空闲周期(气泡),以便更多有用的浮点计算能够占用这些周期。
  • 这些算法已在 AMD 和 NVIDIA GPU 平台上实现。最终优化的 LINPACK 程序在 25,600 个GPU上实现了 107 PFlops 的运算性能(达到了 70% 的浮点运算效率)。
  • 从研究中我们总结出几点见解,对算法设计、编程支持以及架构设计的权衡提供了指导。

结论

  • 新兴的异构并行系统不断为高性能算法的开发带来新的挑战,促使我们从传统的编程方法——即将计算密集型内核简单卸载到加速器上——转向新的策略。本工作展示了一项实践性案例研究,说明了从这一编程方式转变中获益的两个关键方面:(i) 以GPU为中心的任务分配拉取模式,以及(ii) 针对高层算法而非单个内核实施细粒度流水线处理。

    • 推模式与拉模式(Push versus pull mode)

      • 对于第一种策略转变,我们的默认做法是将数据放置在 GPU 端,并持续调用 GPU 内核,无需等待来自 CPU 端的数据传输完成。
      • 就主机-设备编程框架而言,这意味着从推模式向拉模式的转变。
        • 在推模式下,主机(CPU)将数据推送到设备(GPU),然后调用内核。
        • 而在拉模式下,设备(GPU)内核执行后,主机根据需要从设备拉取数据。
          • 由于 CPU 执行的计算通常远少于 GPU,拉模式在大多数情况下能显著减少数据传输量。
      • 随着 CPU 与 GPU 之间速度差距的扩大,从推模式转变为拉模式的好处将愈发明显。
        • 以LINPACK算法的一个实例为例,拉模式算法消除了在矩阵更新尾部所需的数据传输,并在面板分解过程中仅拉取小量数据。
        • 即便是一个直接实现的拉模式算法,也能大幅提高性能。
        • 此外,GPU 端内存空间的不断增加使得拉模式在实际应用中更为合理。
    • 粗粒度与细粒度流水线(Coarse-grained versus fine-grained pipelining)

      • 尽管拉模式消除了部分数据移动开销,但 CPU-GPU 协作算法总会产生数据交换。
      • 为了进一步防止数据移动阻碍 GPU 执行,就需要开发软件流水线算法。
        • 过去,由于不同组件间速度差距较小,粗粒度流水线处理表现良好。
          • 例如,之前的异构程序采用了简单的数据分割策略,在矩阵更新的尾部计算与数据移动之间实现了重叠,并简单采用现有的预取算法在一定程度上实现了通信重叠。
        • 然而,正如我们将在下一节展示的,这种粗粒度流水线由于 CPU 与 GPU 计算之间固有的串行化问题,未能达到良好的可扩展性。
          • 当 CPU 计算、GPU 计算以及 PCIe 和 IB 网络带宽之间存在更大差异时,该问题变得更加突出。
        • 为了尽可能多地重叠长延迟,利用更多并行性的细粒度流水线成为另一种解决方案。挑战在于如何协调这些功能组件之间的计算与数据移动。
  • 此外,我们构建了分析模型以指导算法优化。

    • 我们提出了四种子模型驱动的分析方法,逐步深入地检查 LINPACK 在 CPU-GPU 异构系统上的性能。
    • 经过优化的基准程序在 25,600 个 GPU 上实现了 107 PFlops 的运算性能及 70% 的浮点运算效率,这是同类基于PCIe的GPU集群中报告的最佳性能。
    • 我们相信,从我们的实践经验中,可以进一步优化更多应用程序的性能。最后,我们认为作为衡量超级计算机性能的基准,官方 LINPACK 程序应当针对异构并行系统进行重构。

再读

Section 1 Introduction

  • 随着 CPU-GPU 异构并行架构成为构建超级计算机的主流,随之而来的问题是如何使这一趋势推动大规模并行算法、编程框架及架构设计的新方法。

    • 其中最重大的挑战在于 GPU 计算速度、CPU 计算速度以及数据传输速度(PCIe 和 IB)之间的差距日益扩大。
      • 从 2010 年到 2019 年,一个 CPU 的双精度浮点计算速度从 30GFlops 增长到了 1TFlops,而一个 GPU 的速度则从 250GFlops 跃升至 7TFlops。
      • 实际上,考虑到能效因素,大多数超级计算机的计算节点由较少的 CPU(1-2 个)和较多的 GPU(4-6 个)组成。这种累积效应导致两部分计算速度之间存在一到两个数量级的差距。
    • 除了计算速度的差距,无论是节点内部还是跨节点的数据传输速度都远远落后于计算速度。
      • 例如,PCIe 链路带宽从 Gen 3.0 升级到 Gen 4.0 时翻了一番。同时,高速网络链路带宽从 FDR(2010年)到 HDR(2018年)大约增长了四倍。
      • 相对较慢的 CPU 和数据传输速度常常导致 GPU 出现大量空闲时间,从而降低了大多数算法中的浮点运算效率。因此,一个好的算法应尽可能使 GPU 保持忙碌。这一需求正驱使着高性能算法设计与实现的新途径。
  • 鉴于 LINPACK 程序是衡量超级计算机性能的流行基准之一,我们针对这一新方法对该基准进行了极端优化的研究。

    • LINPACK 是一个使用高斯消元法求解密集线性代数系统 \(Ax=b\) 的大规模并行实现。

      • 该算法的主要部分以分块的方式对系数矩阵进行多次迭代,涉及四个连续步骤:面板分解(PF)、面板广播(PB)、行交换(RS)和尾矩阵更新(TU)。

      • 图 1 概括了自 1993 年第一个稳定版本发布以来 LINPACK 优化方向的演变过程。

        334
        • 图片注解:图 1 展示了从 1993 年至今,在超级计算机上运行 LINPACK 程序的优化方向演变。
      • 毫无疑问,硬件架构的演进是推动基准测试优化的主要动力。在 2015 年之前,人们主要集中在每一代计算引擎(从单核 CPU、多核 CPU 到混合 CPU-GPU)上实现高性能 BLAS,大规模并行算法的骨架几乎没有变化。

      • 近年来,我们见证了一系列关于新型异构并行算法的工作。关键点在于设计高效的流水线算法,以隐藏发生在 PCIe 总线和通信网络(即 IB,后续我们将 IB统称为通信网络)中的数据移动开销。

  • 鉴于不同计算和存储组件之间日益增大的差距和复杂性,我们发现先前的工作中尚未解决两个问题:

    • 首先,现有的流水线深度不足以实现 CPU、GPU、PCIe 和 IB 之间的高度重叠执行。
      • 大部分现有工作实现的是双向流水线:TU 和 PB+PF+RS。
      • 可以通过采用预取策略来实现更多重叠:TU、PF和 PB+RS。
      • 最近的工作在有限程度上利用了更多涉及 RS 的重叠。
      • 但所有这些工作都未曾达到真正的四路流水线:TU、PB、PF 和 RS。我们通过进一步将 PB 从顺序执行流程中分离出来,充分利用了完整的流水线。
    • 其次,难以协调流水线按照预期运行,特别是对于多路细粒度流水线来说。
      • 实际上,四路流水线变得过于复杂,以至于很难人工以非常恰当的方式协调这些细粒度组件。
      • 我们知道,在算法生命周期中,各组件之间的重叠阶段总是在变化,因为每个细粒度组件的持续时间可能以不同的速率变化。因此,预先人工编排的细粒度组件流水线往往会退化为不协调的流水线状态,导致意外停滞。此时,事件驱动的执行模型显得尤为适用。其核心思想是,对于细粒度组件,通过轻量级执行模型尽可能紧密地填充多路流水线,而不是人工确定重叠阶段。本工作中证明,这是一种更高效的流水线策略。
  • 在这项工作中,我们实现了一个专门针对 CPU-GPU 异构架构的全新 LINPACK 程序。与现有工作相比,我们提出了一种结合事件驱动运行时系统的细粒度流水线并行机制,以确定性的方式协调流水线执行。具体而言,本工作对现有文献做出了以下贡献:

    • 我们一步步地设计并实现了四种异构 LINPACK 算法,并证明了算法转变的迫切性。我们提出的第四种流水线算法集成了所有四个宏步骤,以实现完全细粒度的流水线执行。这是首个打破这些宏步骤之间顺序执行顺序的算法(见第 3 节)。
    • 我们实现了一种以 GPU 为中心的编程范式和一个用于线程及流协调的事件驱动运行时系统。这是一个确定性的执行系统,为数值算法框架提供了统一的编程接口。在此确定性编程模型的基础上,一个并行的异构算法能够达成预期的性能表现(见第 4 节)。
    • 我们对管道化算法进行了全面的分析。构建了一个精确的性能模型,以便于对算法和架构进行定量分析。更重要的是,我们总结了若干见解,这些见解指出了算法设计、编程支持以及架构设计权衡的方向(见第5节)。
    • 我们为基于 PCIe 的 GPU 异构系统实现了最高的 LINPACK 评分。优化后的程序在 25,600 个 AMD GPU 上达到了 107 PFlops(70% 的浮点运算效率)。

    基于这些积极成果,我们主张业界应适时发布针对 CPU-GPU 异构架构的新版 LINPACK 基准测试。本文其余部分组织如下:第 2 节介绍 LINPACK 算法的背景及动机;第 3 节详述四种流水线算法;第 4 节描述主要的实现细节;第 5 节报告实验评估结果;第 6 节概述相关工作;第 7 节总结全文。

Section 2 BACKGROUND AND MOTIVATION

2.1 PCIe-Based Heterogeneous System

基于 PCIe 的异构系统

  • 异构计算系统可以通过异构芯片或节点来构建。

    • 前一种架构在一个芯片上集成了通用处理器核心与大量专用加速功能单元(众核架构)。
      • 例如,中国超级计算机“神威·太湖之光”使用了 40,960 颗申威 26010 异构处理器。
    • 后一种架构则通过系统总线将加速器(如 GPU、Phi)与 CPU 相连。
      • 目前,这种连接有多种选择:PCIe、NVLink、CAPI 或 Gen-Z。
      • 迄今为止,PCIe 和NVLink均已成功应用于超级计算机中。
      • 虽然 NVLink 的带宽令人印象深刻,但它要求使用定制的、成本极高的芯片组来实现 CPU-GPU 间的连接,这限制了其广泛应用。到目前为止,唯一能够通过 NVLink 与 GPU 相连的 CPU 是 IBM 的 P9。
      • 得益于 PCIe 生态系统的优势,TOP500 榜单上的大多数 CPU-GPU 超级计算机都是基于 PCIe 总线构建的。
  • 本工作中针对的系统由一个 CPU 和多台通过 PCIe 连接的 GPU 组成。尽管即将问世的 exascale 系统会在 CPU 与 GPU 之间拥有特殊连接(如 AMD 的 Infinity Fabric),但数据移动与计算之间的差距依然显著,因此仍需进行如本工作中所示的细致优化。

2.2 LINPACK Algorithm

LINPACK 算法

  • 算法概述:

    • 给定一个 \(n\times n\) 的系数矩阵 A,LINPACK 算法通过首先计算 \(n\times(n+1)\) 的增广系数矩阵 \(\left[A,b \right]=\left[[L,U\right]y]\) 的 LU 分解(带行部分主元素置换),来求解阶数为 \(n\) 的线性系统 \(Ax=b\)。之后,通过求解三角系统得到解 \(x\)
    • 由于求解器中后一部分的时间开销相对较小,我们的优化重点放在 LU 分解上。
  • 我们省略了基于 BLAS 3 和 BLAS 2 例行程序(如 DGEMM、DGEMV、DTRSM 等)的高性能 LU 分解算法的数学细节,读者可参考文献获取算法证明。为了符号表示的清晰性,LU 分解可以用以下公式表述:

    \[\begin{aligned} \text{A}& =\binom{A_{11}\quad A_{12}}{A_{21}\quad A_{22}}\\ &=\binom{L_{11}\quad\quad\ }{L_{21}\quad L_{22}}\binom{U_{11}\quad U_{22}}{\quad\quad \ \ U_{22}} \\ &=\binom{L_{11}U_{11}\qquad\quad L_{11}U_{12}\qquad}{L_{21}U_{11}\quad L_{21}U_{12}+L_{22}U_{22}} \end{aligned}\qquad(1) \]

  • 在 LINPACK 算法的实现中,分解过程以 \(NB\) 列(一个块)为步长进行迭代。

    • 算法 1 概述了几个主要宏观步骤的迭代,这些步骤调用了最耗时的 BLAS 和 MPI 例行程序。

      335
    • 图 2 描绘了迭代 \(i\) 的一个快照,其中包含了已完成和剩余的部分高斯消元过程。

      336
      • 图片注解:图 2. LU 分解示意图。
    • 在每一次迭代 \(i\) 中,首先通过递归高斯消元对 \(NB\) 列的面板进行因子分解,以获得 \(U_{11}\)\(L_{11}\)\(L_{21}\),这一过程属于面板分解(panel factorization,PF)。

      • 需要注意的是,由于涉及小的子矩阵和 BLAS2 例行程序(例如 DGEMV),此宏观步骤在主机 CPU 上执行。
    • 接下来的两个宏观步骤涉及网络通信。面板广播(PB)将 \(L_{11}\)\(L_{21}\) 发送给同一行中的进程,同时,在行交换(RS)阶段传递相应的行交换信息,以指定列进程如何根据行的主元素进行行交换。

    • 最后,在尾矩阵更新(TU)阶段,通过乘以两个矩阵 \(L_{21}\)\(U_{12}\)(使用DGEMM),对尾随矩阵 \(A_{22}\) 进行更新,这部分计算被卸载到 GPU 上。

      • 这里我们注意到,原本 DTRSM 计算的 \(U_{12}\) 被转换为基于 DGEMM 的实现。
      • 为了提高浮点运算效率,性能调优的最终目标是让所有运行时间都消耗在宏观步骤 TU 上,因为 DGEMM 代表了“有用”的计算,并且容易达到峰值性能。

2.3 Motivation

动机

  • 传统方法:

    • 在移植大多数其他应用程序时,最先进的 CPU-GPU 异构 LINPACK 程序实现倾向于采用一种非常便捷的方法,即将 BLAS 例程替换为 GPU 或混合 CPU-GPU 例程。

    • 这些工作的前提是:

      • BLAS 例程(比如 DGEMM)占运行时间的 90% 以上;
      • 通过 PCIe 总线的数据传输能够在 DGEMM 内部重叠进行,

      这一点在过去仅使用 CPU 或较早的异构系统中是成立的,因为在那时 GPU 速度与 PCIe 数据传输速率之间的差距较小。然而,随着不同硬件组件发展不平衡,这一情况已经发生了改变。

  • 首先,当前存在多个热点,而不只是单一的 DGEMM。

    • 表 1 列出了宏观步骤中涉及的计算和数据移动。

      337
      • 第二列和第三列展示了它们在软件层面上的操作。剩余列展示了它们在硬件层面上的资源需求。
      • 除了众所周知的 PCIe 总线差距之外,网络通信的延迟和 CPU 处理速度较慢也使得情况更加恶化。
    • 图 3 展示了先前系统和我们目标系统的执行时间分布直方图。

      338
      • 图片注解:图 3. 过去系统(仅 CPU 或早期的异构系统)与当前 CPU-GPU 异构系统中四个宏观步骤的执行时间占比。
      • 我们可以看到,从过去到现在发生了根本性的变化。
        • 在以前的系统中,PF(面板分解)、PB(面板广播)和 RS(行交换)的累计执行时间仅占总执行时间的不到 8%。
        • 而目前,由于 GPU 速度过快,它们所占的比例已增加到约 50%。
        • 因此,开发一种新的流水线算法来重叠这些“新崛起”的成本是值得的。
  • 其次,仅在混合 CPU-GPU 的 DGEMM 中利用流水线来隐藏 PCIe 数据传输变得更困难。

    • 设 GPU 的峰值性能为 \(P_{gpu}\) TFlops,PCIe 带宽为 \(BW\ \text{GB/s}\)。假设三个矩阵的尺寸分别为 \(A_{m\times NB}\)\(B_{NB\times n}\)\(C_{m\times n}\)
    • 我们可以建立一个简化的模型来估算执行时间:PCIe 传输时间 \(T_{pcie}=\frac{(m\times NB+NB\times n+m\times n)\times8}{BW\times10^9}\),GPU 计算时间 \(T_{gpu}=\frac{2mnk}{P_{gpu}\times 10^{12}}\)。前提条件是 \(T_{pcie}<T_{gpu}\),并且 \(NB\)(块大小)需要较小,因为我们希望通过增大尾随矩阵来提升 DGEMM 的比例。因此,我们推导出 \(NB>4000\times\frac{P_{gpu}}{BW}\)
    • 在我们的实验系统中(见第 5 节),PCIe 带宽为 \(16\text{GB/s}\);对于峰值性能为 \(4.9 \text{TFlops}\) 的 NVIDIA P100 GPU,\(NB\) 需要大于 \(1225\);而对于峰值性能为 \(5.9\text{TFlops}\) 的 AMD Vega20 GPU,\(NB\) 需要大于 \(1725\)。显然,使用如此大的 \(NB\) 块以获得合理的效率是不切实际的。
  • 要点#1:“将内核例程卸载到加速器上”的免费午餐时代已经结束。因此,对于大多数数值软件程序而言,需要在求解器级别精心设计异构算法。

Section 3 FINE-GRAINED PIPELINING ALGORITHMS

精细流水线算法

  • 为减少因系统各组件速度不匹配导致的 GPU 空闲时间,我们逐步提出了四种递增算法:

    • 单线程粗粒度(STCG)、单线程细粒度(STFG)、多线程细粒度(MTFG)以及多线程全细粒度(MTFG-B)算法。

    • 图 4 描绘了流水线算法的演变过程,其中每个彩色框表示系统组件(GPU、CPU、PCIe 和 IB)被每个宏观步骤占用的时间区间。这些算法逐渐减少了对系统软件调度流水线执行的依赖。

      339
      • 图片注解:图 4 展示了四个逐步发展的流水线算法的快照。
        • 每种颜色的框表示系统组件(GPU、CPU、PCIe 和 IB)被每个宏步骤占用的时间段,其中绿色代表 TU(GPU 上的操作),橙色代表 PF(CPU、IB 和 PCIe上的操作),蓝色代表 RS(PCIe和IB上的操作),黄色代表PB(IB上的操作)。
        • (a) 利用GPU异步流的混合预读取(Hybrid LookAhead)。 (b) 分割RS以与TU重叠执行。 (c) 利用多线程机制推进PF的执行。 (d) PB与PF的重叠执行。
  • 步骤 1. GPU 流预读取:

    • 在原始的 LINPACK 算法中,预读取策略可在一定程度上利用乘法(PF)与更新(TU)重叠执行的机会。

    • 在仅使用 CPU 的系统中,由于 CPU 资源的时间复用,这种好处微乎其微。而在 CPU-GPU 异构系统中,自然可以通过修改预读取算法来利用 CPU 上执行的乘法与 GPU 上执行的更新之间的并行性。

    • 具体来说,尾随矩阵的更新被划分为两个块,分别包含 \(NB\) 列和 \(nq-NB\) 列。在每次迭代 \(i\) 中,当面板 \(i\) 被分解后,算法首先更新 \(NB\) 列的尾随矩阵(TU1),然后立即在 CPU 上开始分解面板 \(i+1\)。这样一来,迭代 \(i+1\) 的面板分解就与迭代 \(i\) 的剩余尾随矩阵更新(TU2)并行执行了。与传统的 LINPACK 算法相比,PF 与 TU 之间的并行化是通过 GPU 的异步流机制实现的。

    • 图 4a 展示了其执行流程及硬件资源占用情况。

      • 每种颜色的框代表一个宏观步骤的操作,其中绿色、蓝色、橙色和黄色分别代表 RS、TU、PF 和 PB 的操作,白色框表示空闲状态。
      • 我们重点关注四个对性能至关重要的硬件组件:GPU、CPU、PCIe 总线和 IB 网络。
        • 管道的启动执行第一次面板分解及其广播,在此过程中,CPU 仅从 GPU 下载一小块数据(如第 4 节中所述的拉模式)。
        • 然后,在每次迭代i中,管道遵循 RS-TU1-(TU2|(PF-PB)) 的序列。在这里,我们使用符号 "-" 表示顺序依赖,使用 "|" 表示并行执行。
  • 步骤 2. 分割行交换:

    • 原始算法在每次迭代的更新操作前对整个尾随矩阵进行行交换,这导致在行交换过程(RS)中 GPU 处于空闲状态。

    • 我们通过将尾随矩阵分割成多个块(数个 \(NB\) 列)来消除这一开销。

      • 这是可行的,因为块 \(i+1\) 的行交换 \(RS_{i+1}\) 独立于块i的更新 \(TU_i\)
      • 因此,合理地加入另一层级的 \(RS_{i+1}-TU_i\) 流水线,使 \(TU_i\)\(RS_{i+1}\) 重叠执行。
    • 图 4b 展示了具有三个块的情况,这足以使行交换与更新操作重叠。

      • 宏步骤 RS 和 TU 被进一步分解为子宏步骤 RS1、RS2 和 RS3,以及 TU1、TU2 和 TU3。
        • 请注意,下一次迭代的 RS1 紧接在当前迭代的 PF-PB 之后安排。
      • 流水线遵循 (TU1 | RS2)-(TU2 | RS3)-(TU3 | (PF-PB-RS1)) 的序列,行交换 RS 被分散到与更新 TU 的重叠执行中。
      • 这种单线程细粒度(STFG)算法成功地在 RS 和 TU 的子步骤之间利用了细粒度流水线,消除了由行交换操作导致的 GPU 空闲时间。
    • 如图 4b 所示,

      • 我们观察到尽管在 RS2 之后 PF-PB 就已经准备就绪,但实际上它被推迟到 RS3 之后才开始。为了使 TU3 与 PF-PB-RS1 重叠执行,分配给 TU3 的尾随矩阵部分明显大于分配给 TU1 和 TU2 的部分。因此,RS3 占据了整个 RS 大部分时间,进而导致 RS 与 PF-PB 的顺序执行。
      • 实际上,我们观察到这种依赖性延迟并非由算法本身造成,因为 RS3 与 PF-PB 之间并没有数据依赖关系。主要原因是我们将所有宏步骤放入了一个单独的线程中,使得它们必须遵循特定的程序顺序执行。
  • 步骤 3. 提前执行PF:

    • 为去除 RS 与 PF-PB 之间不必要的顺序执行,我们利用多线程机制提前执行 PF。如图 4c 所示,
      • PF-PB、RS 和 TU 被分配到三个线程中。
      • PF 和 RS 在两个并行执行流程中执行,这样 PF 就能在 TU 线程完成 PF 所需面板数据更新后立即开始。
      • 我们认为,创建多个线程来适应不同的宏步骤是合理的,因为四大主要硬件组件(CPU、GPU、PCIe 和 IB)可以独立工作。
      • 在大多数流水线生命周期中,宏步骤在不同的硬件组件上执行。
        • 例如,TU 完全在 GPU 上执行,PF 主要在 CPU 上执行,而 RS 和 PB 则占据了 IB 大部分时间。
    • 提前执行 PF 和多线程机制带来了两个问题。
      • 首先是线程和流的协调。
        • 图 4c 中的黑色箭头表示为了确保算法正确性所需的线程和流同步。
        • 如何高效实现这种协调并带来很小或没有开销,对浮点运算效率有显著影响。
      • 第二个问题是通信协调。
        • 如图 4c 所示,PF 和 RS之间可能存在严重的通信争用,这可能会降低浮点运算效率。
        • 这里我们省略了实现同步运行时系统的细节,并将其留待下一部分讨论。
  • 步骤 4. PB 与 PF 重叠执行:

    • MTFG 算法成功实现了 TU、RS 和 PF-PB 的并行处理。

      • 在 PF-PB 所需时间超过 TU 的情况下,PF-PB 就成了瓶颈。
      • 回顾表 1 中的资源需求,PF 的特点是低并行度的 CPU 计算负载和较轻的 IB 通信负载,而 PB 主要特点是重 IB 通信负载。有机会在 PF 的过程中插入 PB,以减少 PF-PB 的时间,同时不增加 PF 的时间。
    • 我们提出了一种改进算法,通过进一步将面板广播分解为几个独立步骤,使之能与面板分解重叠执行。

      • 在最初的 HPL 实现中,面板广播在面板分解之后立即顺序执行。

      • 考虑到面板中第 \(i\) 列的广播与第 \(i+1\) 列的 LU 分解是相互独立的,我们将面板划分为多个子块(在 HPL 程序中为 \(nbmin\) 列)。

        • 根据经验模型,子块的数量设定为 4。
      • 因此,宏步骤 PF-PB 以流水线方式排列,即 PF1-(PB1|PF2)-(PB2|PF3)-(PB3|PF4)-PB4,如图 4d 所示。子块数量定为 4 是基于 \(\text{NB}\) 值通过暴力枚举实验确定的。

      • 算法 2 详细介绍了 MTFG-B 算法的实现。

        340
        • 主线程(线程 0)通过完成第一个面板的分解和广播来启动流水线(第 2-7 行)。
          • 为了通知行交换线程(线程 1)和更新线程(线程 2),线程 0 提交必要的信号(第 6-10 行)。
        • 在算法的主循环中,线程 0、1 和 2 分别并行执行 PB-PB(第 17-23 行)、RS(第 27-32 行)和 TU(第 36-40 行)的算法流程。
          • 同步原语 wait 和 commit 的实现细节在第 4 节描述。
        • 需要注意的是,拆分面板广播可能会加剧 PF、RS 和 PB 之间的通信争用。但由于通信协调机制,我们实现了这些 IB 通信的低开销调度。
        • 需要注意的是,我们未使用多线程 PF,原因有二:
          • 多线程 PF 将与多线程 BLAS DGEMM 产生争用;
          • PF 的计算负载太小,无法有效利用更多线程。

Section 4 GPU-CENTRIC PROGRAMMING

以 GPU 为中心的编程

4.1 GPU-Centric Paradigm

以 GPU 为中心的范式

  • 在异构编程模型中,卸载内核的副作用是大量数据(大矩阵)需要通过较慢的 PCIe 总线传输,以至于传统的软件流水线技术不再能很好地发挥作用。

    • 根本缺陷在于异构算法设计中的以 CPU 为中心的范式。如图 5 所示,以 CPU 为中心的范式将大矩阵推向 GPU,给主机 CPU 与设备 GPU 间的数据传输带宽带来了巨大压力。

      341
      • 图片注解:图 5 展示了从推送模式到拉取模式的方法转变。内核:在 PB(面板广播)和 RS(行交换)中,存在一些辅助函数,它们在计算上较为密集。
    • 相比之下,我们倾向于采用以 GPU 为中心的范式,其中 CPU 仅在算法生命周期中从拥有所有数据的 GPU 中提取小矩阵。

  • 此外,拉模式为加速 GPU 中的计算密集型内核提供了更多机会,并易于利用 NVLink 和 GPUdirect 来减少数据移动开销。在某种程度上,NVIDIA 近期版本的 LINPACK 确认了这一趋势的优势,它提供了一个选项,可以直接在 GPU 内存中分配矩阵以提高性能。

4.2 Event-Driven Paradigm

事件驱动范式

  • 为了支持细粒度流水线算法中并发线程和异步流的执行,我们根据依赖关系引入了事件驱动的执行模型。

    • 存在三个线程执行四个宏观步骤:PF-PB 线程、RS 线程和 TU 线程。每个任务包括一个 GPU 流事件和一个 CPU 线程信号量。

    • 如图 6 所示,该模型被实现为一个生产者-消费者系统,创建了两套任务队列以便尽可能快地推进 RS 步骤。

      342
      • 图片注解:图 6. 基于生产者-消费者模型的事件驱动执行。
      • 需要注意的是,第 \(i\) 次迭代的 PF 步骤依赖于第 \(i-1\) 次迭代第一块的更新,这里为了简化说明而省略。
      • 在第 \(i\) 次迭代过程中,PF 线程在完成前瞻面板分解后立即通知 RS 线程执行行交换。随后,RS 线程从前一次迭代的 TU 线程那里获取一个 RS 任务(步骤 1 和 5),该任务由 TU 线程在之前步骤中提供(步骤 4 和 8)。
        • 执行 RS 任务的过程包括 IB 通信、PCIe 数据传输以及异步调用 GPU 交换内核。RS 线程在完成获取的 RS 任务后,将相应的 TU 任务放入自己所在迭代的 TU 队列中(步骤 2 和 6)。
        • 类似地,TU 线程获取一个 TU 任务(步骤 3 和 7),并在完成该 TU 任务后,将相应的 RS 任务放入下一迭代的 RS 队列中(步骤 4 和 8)。
          • TU 任务包含纯计算负载,TU 线程通过异步调用 GPU TRSM 和 GEMM 内核来执行它。生产者-消费者模型保留了所需的依赖关系。
  • 为了以较低的开销实现生产者-消费者模型,我们使用信号量和流事件机制来维护虚拟任务队列。

    • 创建并存储在固定长度数组中的多个信号量和 GPU 流事件被用来模拟队列。这些数组的长度是可能的最大块数。

    • 我们利用信号量来实现线程间的协同,避免了在线程间共享任务对象的开销。

    • 值得一提的是,流事件同步机制遵循先进先出的原则。

      343
      • 图片注解:图 7. 同步机制的图示比较。(a) 展示了意外停顿是如何发生的,(b) 展示了如何解决这一问题。

      • 如图 7a 所示,如果在同一个流中相继执行的内核 A 和 B 之后记录了事件 A 和 B,即使内核 A 比内核 B 先完成,如果先调用同步 B 再调用同步 A,那么同步 B 也应该在同步 A 之前完成,这导致了图(a)中 t2' 到 t3 的意外停滞。

        • 应当注意这一原则,因为在不同线程中调用时,无法预知哪个同步会被先调用。为了解决这个问题,我们使用信号量来确保按照先进先出的原则顺序调用同步。
      • 如图 7b 所示,通过信号量强制同步 B 等待同步A完成。

  • 为了解决在不同异构平台上的可移植性问题,我们为 LINPACK 实现设计了一个轻量级的跨平台框架 HPCX1。

    • 尽管以往的工作使用通用编程模型(如 OpenCL)来实现跨平台的 LINPACK 基准测试,但它们面临着 level-3 BLAS 例程效率低下的问题,这对于 LINPACK 基准测试至关重要。

    • 有一些运行时系统,如 StarPU/ParSEC,能够提供事件驱动调度的能力,但复杂的接口和厚重的软件栈阻碍了我们在 LINPACK 实现中采用它们。

    • 相反,我们寻求一个针对 LINPACK 的轻量级运行时系统。因此,我们抽象了 LINPACK 中所需的一些通用编程接口,如图 8 所示。

      344
      • 图片注解:图 8 展示了 HPCX 框架。我们算法优化的实现是基于 HPCX 框架的抽象层进行的。
    • 在底层,我们采用供应商提供的不同编程模型来利用高性能 BLAS 库和新硬件特性,如 GPUdirect。HPCX 框架将设备特定的实现与我们的算法优化解耦。当我们把优化过的 LINPACK 移植到新型加速器上时,只需要对这些抽象的新实现(或仅仅是新封装)即可。

Section 5 EVALUATION

评估

  • 我们的测试平台由 6400 个计算节点组成,每个节点配备有一颗 AMD CPU(配备 128GB 主内存的 32 核心 AMD EPYC 7551P 处理器)和四块最新的 AMD GPU(Vega20)。

    • 系统中使用的 GPU 是由 AMD 定制的特殊型号,固定在较低频率(1.4 GHz)运行。一颗 CPU 提供 384 GFlops 的计算能力,而一颗 GPU 则提供 5.9 TFlops 的计算能力。
    • 在每个节点上,CPU 侧有 128 GB 的 DDR4 内存,GPU 侧有 64 GB 的 HBM2 内存。HBM2 内存的带宽高达 1 TB/s。
    • 所有节点通过一个高带宽 FatTree 互连网络连接,该网络配备有 200 Gb/s 的高速网络接口。
    • 在一个节点内,两个计算单元通过 PCIe 总线(Gen 3.0)相连。
    • 总计,有 25600 块 GPU 共同提供了 152 PFlops 的峰值性能。直到 2020 年,这是世界上最大的基于 PCIe 的 GPU 异构系统。
  • 在实验中,我们使用了最近的 BLIS 和 rocBLAS 库来加速 DGEMM。因为最大峰值性能取决于块大小,所以在实验中,我们使用了一组块大小(128, 192, 256, 384, 512, 1024),并选择了其中一个(256)以达到最大峰值性能。

  • 亮点:

    我们在两个异构系统上实现了所有提出的算法。在这里,我们重点介绍实验中展示的几个关键点。

    • 新算法具有良好的可扩展性

      • 从 4 块到 25,600 块 GPU,其浮点运算效率仅损失了大约 5%。最后,在固定的 CPU-GPU 频率下,它运行 LINPACK 基准测试达到了 107 PFlops 的分数,效率为 70%。
    • 我们的上限分析揭示,一个平衡的计算系统优于盲目提供更强大的 GPU。

      如果 LINPACK 程序都不能很好地利用 GPU,更不用说其他算术强度更低的真实世界应用了。

5.1 Experimental Results

实验结果

5.1.1 Overall Performance

总体绩效

LINPACK 得分是通过每秒浮点运算次数(Flops)来衡量的。

  • 首先,我们在大规模集群系统上检验 LINPACK 得分。

    • 为了性能测试,问题规模 \(N\) 随着节点数量的增加而调整,以保持大约占 GPU 总物理内存 80% 的内存消耗。
  • 图 9 展示了随着 GPU 数量从 4(1 个节点)扩展到 25,600(6,400 个节点)时的 LINPACK 得分(单位为 TFlops)。根据并行实现和运行时配置,每个 MPI 进程映射到一个 GPU 上。

    345
    • 图片注解:图 9 展示了性能与可扩展性的实验结果。
  • 在 25,600 个GPU上实现的浮点运算性能为 107 PFlops,其 70% 的浮点运算效率高于类似的 ABCI 系统。

  • 并行效率保持在 92% 以上,从 4 个 GPU 扩展到 25,600 个GPU时,效率从 99% 略微下降。

  • 鉴于 FatTree 互连的系统架构,进程间通信会经过网络的所有层级。在计算节点更多的情况下,如果在根层级增加更多的交换机,扩展性可以保持稳定。

5.1.2 Incremental Optimization

渐进式优化

本文逐步介绍了四种流水线算法。优化策略逐级添加到基线算法中。为了进一步理解每次优化带来的性能提升,我们在一个计算节点上分析了它们的运行时行为。这是一个合理的方法,因为我们能够对关键性能因素进行剖析,包括通过PCIe总线和网络的数据传输。

  • 从推送转向拉取的方法通过基于拉取模式的异构 LINPACK 算法的直接实现得到了验证。

    • 与推送模式不同,GPU 在其内存上持有所有数据,并默认执行所有计算。只有当某些内核并行度较低,导致在 GPU 上的性能低于 CPU 时,才会将它们调度到 CPU 上执行,同时伴随它们之间少量的数据移动。

    • 图 10 显示,简单的拉取模式显著提高了性能。作为定量比较,推送模式的数据移动量为 \(2n(n+1)+n\times NB+NB\times (n+1)+NB^2\) 个双字,而拉取模式的数据移动量则减少到了 \(2n\times NB+2NB\times (n+1)+NB^2\)

      346
      • 图片注解:图 10 展示了从推送模式到拉取模式的性能提升。推送模式算法的性能受限于 PCIe 总线带宽,且随着问题规模的增大增长缓慢。
  • 拉取模式的一个限制因素是需要更大的 GPU 内存,而 GPU 内存比 CPU 内存更昂贵。目前,一种替代方案是采用基于 NVLink 的内存一致性架构,通过可比较的高带宽直接访问 CPU 内存。

  • 要点#2:无论是增加加速器内存的容量还是采用类似 NVLink 的内存一致性架构,都促使数据布局方法从以 CPU 为中心(推送模式)向以 GPU 为中心(拉取模式)转变。

  • 图 11 比较了四种算法的 LINPACK 得分。

    347
    • 图片注解:图 11 展示了四种流水线算法在 TFlops 性能上的逐步提升。
  • 结果显示,随着流水线粒度的细化,得分逐渐提高。

    • 通常情况下,问题规模 N 设置得足够大,以占用超过 80% 的物理内存。为了检验我们算法的适应性,实验中 N 的大小从占用约 60% 内存的(72,192)开始。为了验证流水线效果,我们使用内部性能分析工具生成了执行的可视化跟踪。图 12 展示了两个迭代中的执行快照。

      348
      • 图片注解:图 12 展示了从 GPU 视角看四种流水线算法的执行快照。每个水平条代表一个流,彩色部分代表不同的 GPU 内核。红色和紫色部分分别是 TU 中的 DGEMM 和 DTRSM 内核。黄色、蓝色和粉色部分是 RS 中的内核。橙棕色和靛蓝色部分是数据传输内核。其他部分是占用边缘 GPU 时间的小辅助内核。两个红色部分之间的较小气泡意味着浮点计算效率更高。
    • 通常情况下,随着问题规模的增大,所有程序都会实现更好的性能。单线程算法仅依赖于并发流的异步执行。细粒度流水线(STFG)并未实现完美的重叠。

      • 如图 12a 所示,在 STCG 中,两个 GPU DGEMM 执行之间存在一个大的空闲周期(bubble)。即使 STFG 使 RS 的一部分与 TU 重叠,这个空闲周期的停滞并没有得到太大改善。由于其固有的串行化,异步流机制并不能很好地实现并行化。
      • 多线程算法(MTFG)利用了更细粒度的重叠,从而挤压并减小了这个空闲周期,如图 12c 所示。
      • 进一步对细粒度广播的优化使得流水线执行接近完美重叠的状态。如图 12 所示,由于实验平台上 CPU 执行较慢,两个迭代之间只存在很小的空闲周期。我们认为,如果使用功能更强大的 CPU 来高效支持计算和通信的并行化,这个空闲周期将变得微不足道。
  • 要点 #3:在当前的异构系统软件栈中,非确定性的执行顺序会导致性能下降。期望事件驱动模式能保持算法层面定义的执行顺序,以此来缓解这一问题。

5.2 Performance Analysis

性能分析

5.2.1 Analytical Model

分析模型

  • 假设矩阵 \(A_{n\times(n+1)}\) 在逻辑上呈 2D 网格排列的 \(P\times Q\) 个处理器之间均匀划分,划分为 \(NB\times NB\) 个块,其中 \(NB\) 是块的大小。网络通信的延迟和带宽分别表示为 \(\alpha\)\(\beta\)。在第 \(i\) 次迭代中,每个处理器拥有 \(mp\) 行和列 \(np\),随着分解从第一次迭代进行到最后一次迭代,这个数量会逐渐减少。

  • 在面板分解中,我们通过近似矩阵乘法中的浮点运算次数乘以一个因子 \(f_{pf}\) 来估算计算量。

    • 这是合理的,因为在 PF 中应用了一个基于矩阵乘法的递归算法(面板被划分为 \(nbmin\) 列的子块),这占了大部分的浮点运算。

    • \(nbmin\) 列的子块中,DGEMV(矩阵向量乘法)的时间开销可以忽略不计。面板分解的时间成本估计如下:

      \[T_{pf}=\frac{f_{pf}\times mp\times NB^2}{P_{cpu|gpu}*E_{cpu|gpu}},\qquad(2) \]

      • 其中, \(P_{cpu|gpu}\)\(E_{cpu|gpu}\) 分别表示理论上的浮点运算峰值性能和平均效率,
      • \(cpu|gpu\) 表示面板分解中的 BLAS 例行程序是在 CPU 上还是在 GPU 上执行。
    • 面板广播的成本取决于广播算法的实现。由于其较低的复杂度,我们使用了 HPL 包中的 Long 算法。其执行时间估计如下:

      \[T_{pb}=\left(\frac{log_2^Q}2+\frac1Q+1\right)\times\left(\alpha+8\times\frac{mp\times NB+NB^2+NB+1}\beta\right).\qquad(3) \]

  • 第二大通信步骤是与枢轴行交换行。采用了一种称为 spread-roll 的算法来减少通信次数,其执行时间估计如下:

    \[T_{rs}=\left(\frac{log_2^P}{P}+2)(\alpha+8\times\frac{nq\times NB}{\beta}\right).\qquad(4) \]

  • 最耗时的部分是对尾随矩阵进行更新。

    • 每个进程并行执行 DGEMM(General Matrix Multiply)和 DTRSM(Triangular Solve with Multiple Right-Hand Sides)例程。

    • 在异构实现中,这些操作在 GPU 上进行了加速。执行时间估计如下:

      \[T_{tu}=\frac{2\times mp\times nq\times NB}{P_{gpu}\times E_{dgemm}}+\frac{nq\times NB\times NB}{P_{gpu}\times E_{dtrsm}},\qquad(5) \]

      • 其中, \(E_{dgemm}\)\(E_{dtrsm}\) 分别代表 GPU 上 DGEMM 和 DTRSM 的浮点运算效率。
  • 图 13 展示了每个迭代中每个宏观步骤的实验时间和建模时间。预测误差小于 5%。这一极其微小的差异表明该模型对于分析而言足够准确。

    349
    • 图片注解:图 13 展示了四个宏观步骤的实验结果(右侧)与模型预测结果(左侧)之间的性能差异非常小,这表明我们的性能模型是准确的。
  • 基于每个宏观步骤的建模时间,我们为所提出的算法构建了性能模型(图 14)。STFG 算法尝试将 CPU 上的 PF 与 GPU 上的 TU 重叠。

    350
    • 图片注解:图 14 展示了四个算法的性能模型。(a)中,\(T_{STCG}=T_{rs}+\max(T_{pf}+T_{pb};T_{tu})\);(b)中,\(T_{STFG}=\max(T_{pf}+T_{pb}+T_{rs};T_{tu})\);(c)中,\(T_{MTFG}=\max(T_{pf}+T_{pb};T_{rs};T_{tu})\); (d)中,\(T_{MTFG-B}=\max(T_{pf};T_{pb};T_{rs};T_{tu})\)。这里的 \(T_{pf}, T_{pb}, T_{rs}, T_{tu}\) 分别代表面板分解、面板广播、行交换和尾随更新步骤的执行时间。

    • 如图 14a 所示,

      • 在早期迭代(t1 之前),当 \(T_{tu}>T_{pf}+T_{pb}\) 时,PF-PB 与 TU 完美重叠。
      • 随着迭代算法的进行,PF-PB 的时间超过了 TU 的时间,每次迭代的时间将主要由 RS-PF-PB 决定。

      STCG 的理想执行时间是:

      \[T_{SICG}=T_{rs}+max(T_{pf}+T_{pb},T_{tu}).\qquad(6) \]

    • 图 14b 展示了 STFG 算法的性能模型。

      • 在迭代 t1(大约第 50 次迭代)之前,STCG 中由 RS 暴露的空闲气泡被消除。
      • 然而,随着尾随矩阵的减小,重叠比例开始下降,TU 的时间不能覆盖 RS-PF-PB 的时间。
      • 最终,在迭代 t2 之后,当 PF-PB 的时间超过 TU 的时间时,STFG 退回到 STCG。

      因此,STFG 算法的执行时间估计如下:

      \[T_{STFG}=max(T_{rs}+T_{pf}+T_{pb},T_{tu}).\qquad(7) \]

    • 在 MTFG 算法中,RS 和 TU 都被细分为细粒度的重叠管道。

      • 如图 14c 所示,在最初的 150 次迭代(t1 之前)中,迭代时间主要由 TU 的时间主导,这相比于 STFG 的 50 次迭代有所增加。
      • 过了迭代 t1 之后,PF-PB 成为瓶颈。

      MTFG 预期的性能为:

      \[T_{MTFG}=max(T_{rs},T_{pf}+T_{pb},T_{tu}).\qquad(8) \]

    • MTFG-B 算法已经将所有四个宏观步骤完全流水线化,其执行时间主要由 \(T_{pf}\)\(T_{pb}\)\(T_{tu}\)\(T_{rs}\) 中的最长时间决定。MTFG-B 算法的性能模型如图 14d 所示。我们观察到,直到迭代 t1 之前没有出现瓶颈,此时由于 CPU 速度较慢,PF 成为了瓶颈。

      \[T_{MTFG-B}=max(T_{rs},T_{pf},T_{pb},T_{tu}).\qquad(9) \]

    要点 #4:精细的算法不应完全依赖系统软件,而应确定性地编排流水线执行,以充分利用硬件资源之间的潜在重叠。

5.3 Inefficiency Source

效率低下的来源

  • 乍一看,我们细粒度流水线算法的性能并不令人惊讶。如图 11 所示,随着 \(N\) 的增加,性能平稳增长,当 \(N\) 达到最大值(88,576)时,浮点运算效率最终达到 76.6%。

  • 这种效率损失主要归因于两个原因:

    • 结合性能模型的分析,可以确定算法距离上限还有多远。流水线算法有两个级别的上限。

      • 第一个是通用上限,它由 BLAS 例行程序决定。为了建模的简单性,我们通过估计 LINPACK 程序中使用的 DGEMM 内核的平均浮点效率来设定这个上限。对于 AMD Vega20 GPU,DGEMM 的浮点效率大约为 88%。MTFG-B 算法达到了通用上限的 87%,这低于大多数仅 CPU 同构系统实现的效率(超过 90%)。

      • 实际上,性能模型还为每种流水线算法指出了另一层次的个体上限。

        • 这些个体上限源自分析性能模型,这些模型通过公式(6)、(7)、(8)、(9)形式化表示。图 15 绘制了所有四种算法的个性界限。

          351
          • 图片注解:图 15 展示了根据我们的性能模型,为每个算法设定的理论性能界限。
        • STCG 算法实现了 57.7% 的效率,这非常接近其个性界限 58.8%。

        • 通过细粒度流水线,STFG 算法重叠了部分行交换通信,实现了更高的 64.8% 效率,这也接近其个性界限 65.2%。实际性能与理论界限之间几乎完美的匹配,是因为单线程实现的额外开销较低。相比之下,虽然多线程实现可以达到更高的性能,但由于线程和异步流交互产生的开销,它们会有更大的偏差。

  • MTFG算法达到了 70.2% 的效率,这略低于其个性界限 75.7%。性能偏差来源于两个方面。

    • 首先,在多线程流水线中,有几个 CPU 核心专门用于行交换和尾随矩阵更新的流水线,因此用于计算面板分解的核心数量较少。结果,扩大的面板分解(PF)导致流水线中出现了更多的气泡(即空闲周期)。
    • 其次,为了支持多线程与异步流的混合使用,实现了一个生产者-消费者机制,这引入了线程同步的额外开销。MTFG-B 算法对其个性界限的偏差稍微更大一些。除了与 MTFG 算法相同的原因之外,细粒度的面板分解还引入了潜在的网络带宽争用问题,这发生在面板分解(PF)和行块更新(PB)中的 MPI 通信操作之间。

5.3.1 System Bottleneck Analysis

系统瓶颈分析

  • 如图 15 所示,我们最佳的 MTFG-B 算法的个体界限与实际上限(DGEMM 的效率)之间存在较大差距。我们想要确定限制我们优化算法潜力的内在系统瓶颈。

    352
    • 图片注解:图 16 展示了不同系统配置对最佳 MTFG-B 算法效率的影响。
  • 计算平衡

    • 与大多数 CPU-GPU 异构架构中的并行算法类似,LINPACK 程序利用协同并行,其中 CPU 和 GPU 同时执行计算。
    • 从浮点效率的角度来看,我们观察到了随着 CPU 或 GPU 速度变化的有趣趋势。如图 16a 所示,随着 CPU 速度从 12 GFlops(我们系统的配置)增加到每核心超过 40 GFlops,LINPACK 效率并未持续上升。
    • CPU 过慢或过快都会损害整体效率。具体来说,
      • CPU 速度慢会导致 GPU 在等待依赖计算和通信完成时出现停顿;
      • 而 CPU 速度过快则由于PF的计算工作负载小而未得到充分利用。
    • 请注意,我们系统中 CPU 慢和 GPU 快的配置是我们算法上限受限的原因之一。当 CPU 速度翻倍(从每核心12 GFlop/s增加到每核心24 GFlop/s)时,MTFG-B 算法的潜在效率可以达到 83%,这是上限的 94.3%。

    要点#5:在异构系统中的协同计算模式中,主机与加速器之间的计算平衡有助于实现更高的效率。

  • GPU内存需求大

    • 图 16b 展示了随着 GPU 内存大小增加时(其他系统配置保持不变)MTFG-B 算法效率的趋势。
      • 直观上,随着 GPU 内存大小变大(\(N\) 增大),效率会提高。当 GPU 内存大小增加到 32 GB 时,效率达到 81.9%,这是上限的 93.1%。这一结果表明,只要 GPU 内存足够大,我们的优化算法几乎消除了所有数据传输的开销。
    • 增加主机 CPU 内存的情况则不同,因为以 CPU 为中心的算法设计效率受限于较慢的 PCIe 总线。
      • 如图 10 所示,基于推送模式的算法从更大的主机 CPU 内存(N增大)中获得的边际改进很小,而且强大的 GPU 导致了这样一个情况:通过 PCIe 的数据传输无法通过 GPU 上相应的 DGEMM 调用来隐藏。
      • 除非 CPU 和 GPU 之间有更快的连接方式,比如 NVLink,否则 CPU 内存中的数据无法被 GPU 有效访问。
      • 换句话说,考虑到较慢的 PCIe 总线,仅仅增加 CPU 内存并不能帮助提高 GPU 的利用率。

    要点#6:更大的GPU直接访问内存是缓解由于慢速PCIe总线造成的数据传输瓶颈最有效的方法。

5.4 Comparison on NVIDIA GPU

英伟达™(NVIDIA®)图形处理器比较

  • 此外,我们还在其他相对应的平台上展示了性能比较。由于硬件平台和实现方式的限制,我们提供了一个简要的比较而非全面的实验评估。

    • 我们在基于 NVIDIA GPU 的异构系统上实现了相同的优化算法。
    • 由于物理资源的限制,实验在由 16 块 GPU 组成的集群上进行。该集群包含 8 个计算节点,每个节点由一个 Intel Xeon CPU 和两块 NVIDIA Kepler GPU 组成。
    • CPU 与 GPU 之间的连接同样基于 PCIe 总线,而非 NVLink。
  • 图 17 比较了随着问题规模和GPU数量增加时,它们的效率。

    353
    • 图片注解:图 17 显示了在 NVIDIA GPU 上的性能比较。

    • 为了公平起见,我们通过搜索矩阵大小和分块大小组合的空间来选择最大性能。

    • 我们的算法平均比 NVIDIA LINPACK 高出 5% 的效率。实验表明,我们的算法在不同的 GPU 集群间具有良好的性能可移植性。

    • 由于 NVIDIA LINPACK 是一个闭源程序,且缺乏关于其实现细节的公开文献,因此不可能对其性能差异进行深入分析。然而,基于其性能与 MTFG 算法相似的观察,我们相信它并没有像我们一样实现完全的流水线算法。

本节回顾了针对 CPU-GPU 异构系统上 Linpack 优化的异构优化和建模相关研究。随后,我们将讨论最先进的 LU 分解算法优化技术,并在此背景下探讨这些技术如何与 Linpack 基准测试相关联。

  • 针对 HPL 的具体优化

    • 早期,最有效且广泛应用的利用 GPU 计算能力的做法是采用高度优化的(混合-)BLAS 库。
      • Fatica 等人实现了基于 CUDA 的第一版 NVIDIA GPU 集群上的 HPL,他们利用 CUDA 提供的异步操作来重叠计算和数据移动。
      • M.Bach 等人也提出了针对 AMD GPU 集群优化的 HPL 版本。
      • 另一方面,T.Endo 等人采用了一种在 CPU 和 GPU 节点上启动不同数量 MPI 进程的方法来处理节点间的异质性。
      • Rong Shi 等人提出了一种两级工作负载划分策略和进程网格重排,以高效利用异构 CPU-GPU 集群中的所有计算资源。
    • 此外,针对 HPL 基准的调度策略也已得到研究,
      • 例如支持异步并行和异构性的框架 StarPU 和 OmpSs。HPL 任务的静态和动态调度也被讨论。
      • 关于编程模型的全面介绍,可以参考M.Abalenkovs 等人的工作。
      • 在本文中,我们专注于节点内的异质性,并提出了一个基于深度分解求解器算法的全流水线实现的事件驱动执行模型。
  • 表 2 总结了我们的技术与另外三个同类技术之间的显著差异。

    354
    • HPL-GPU 是一个项目,
      • 它利用增强的预取和高性能的 GPU DGEMM。它利用异步 GPU 命令队列,允许动态地将其他 CPU 任务卸载到 GPU上。HPL-GPU 是一个优秀的工程作品,专注于数据移动问题,并实现了软件流水线算法来隐藏早期 AMD 和 NVIDIA GPU 上的数据移动开销。
      • 对于本工作中针对的新一代 AMD GPU,HPL-GPU 无法工作,因为编程模型已从 CAL 演进到 Roc/HIP。更重要的是,HPL-GPU 没有处理像我们这样复杂的同步情况。
      • 相比之下,我们实现了一个事件驱动的调度系统,以利用更细粒度的流水线并行性。
    • 在[21]中,除了针对 Intel Xeon Phi 的高度优化的 DGEMM 内核外,作者开始关注 CPU 和加速器之间的工作负载划分和调度问题。
      • Fatica 静态地将尾随矩阵分割为 CPU 部分和 GPU 部分,然后实现两个混合 BLAS 例程(DGEMM,DTRSM)来加速 LINPACK 基准测试中的努力致力于通过静态或动态工作负载分配来平衡 CPU 和 GPU 之间的负载。背后的原因是主机 CPU 相比 GPU 提供了可比的 FLOPS。
      • 如表 2 所示,只有我们的方法在 PF、PB、RS、TU 之间实现了完全的流水线并行性。
  • LU分解

    作为 Linpack 算法的核心,基于高斯消元的 LU 分解算法已经被广泛研究。

    • S. Tomov 等人和 J. Dongarra 等人为单节点级 LU 分解实现提供了一些深刻的优化。在单节点情况下,无需在节点间通信/计算重叠上花费精力,而这正是我们面临的主要问题之一。
    • 同时,如 [26]、[46] 中使用的特殊选主元方法等技术,由于数值稳定性问题,不适用于 Linpack。
    • A. Heinecke 等人和 G. Jo 等人注意到了节点间通信开销,并通过将 RS 静态分割成多个部分并与尾随矩阵更新重叠来缓解这一问题。
      • 我们与他们的想法相似,但不同之处在于我们自适应地进行此操作,并在 GPU 上加速了 RS。
    • 然而,他们都留下了 PB 作为瓶颈,这是影响大规模集群上算法可扩展性的关键因素。他们全都提到了多线程机制,但我们以截然不同的方式使用它。受到 SLATE的启发,我们实现了一个类似的多线程事件驱动调度系统,来协调不同类型的工作负载(计算、通信)在不同硬件(CPU、GPU、PCIe和IB网络)上的执行。
  • 建模

    • J. Dongarra 等人已经针对同构系统上的 Linpack 基准测试提出了一般的性能模型。
    • 以往模型的目标是预测 Linpack 在同构并行系统上的可扩展性。我们的性能模型建立在 LU 分解中与流水线相关的宏观步骤之上,以便可以直接用于性能和低效分析。
posted @ 2024-06-11 10:29  AncilunKiang  阅读(148)  评论(0编辑  收藏  举报