在这篇文章中,我将介绍最简单、最高效、最可移植的加速计算方式。即三种可以用于 GPU 编程的方式。
图 1 对 NVIDIA 平台进行编程的三种方式
CUDA C++ 和 Fortran 是英伟达能够展示新硬件和软件创新的创新基础,在这里你可以调整你的应用程序以实现在 NVIDIA GPUs 上的最佳性能。许多开发人员认为,这就是英伟达期望每个人为 GPU 编程的方式。
相反,我们希望首次使用 NVIDIA 平台的开发人员能够使用标准的并行编程语言,如 ISO C++、ISO Fortran 和 Python。在这篇文章中,我将重点介绍一些使用这种方式进行并行编程的成功案例,以展示进入英伟达 CUDA 生态系统最有效的途径。
英伟达战略的基础是提供一套丰富且成熟的 SDK 和库,可以基于它们构建应用程序。英伟达已经提供了高度优化的数学函数库,例如 cuBLAS、cuSolver 和 cuFFT;核心库,例如 Thrust 和 libcu++;通信库,例如 NCCL 和 NVSHMEM,以及其他可以在其上构建应用程序的包和框架。
除此之外,英伟达还分层了三种不同的编程方式:
- 标准语言并行开发,这是本文的主题
- 平台专用语言,如 CUDA C++ 和 CUDA Fortran,以在 NVIDIA 平台上获得最佳性能
- 编译器指令,通过启用增量性能优化来弥合这两种方式之间的差距每种方式都在性能、生产率和代码可移植性方面进行权衡。由于它们都可以互操作,所以你不必使用特定的模型,但可以根据需要混合使用任一或所有模型。
如果你是以使用标准编程语言中的并行来开始编写代码的,那么你可以使用 NVIDIA 平台或任何其他已经具有并行运行能力的基线代码平台。这就是为什么我们投入了十多年的时间来与标准语言委员会合作,采用特性来实现并行编程,而不需要额外的扩展或 API。标准语言并行开发是一股水涨船高的潮流,能让所有人都感到振奋。
ISO C++
在最近的编程趋势研究中,C++ 编程语言一直是顶级编程语言之一。它在科学计算方面的使用有了显著的增长。其标准模板库的丰富性使其成为一种用于新代码开发的高效语言,并且自 C++ 17 发布以来,它已支持并行编程的多个重要特性。
我已经看到一些应用程序在重构时摒弃了传统的 for 循环,转而采用这些 C++ 的并行算法。以下是其中一些的结果。
Lulesh
Lulesh 是劳伦斯利弗莫尔国家实验室( Lawrence Livermore National Laboratory,LLNL)的一个流体动力学迷你小应用程序,用 C++ 编写。这个迷你应用程序有多个版本,可用于评估不同的编程方式,包括代码质量和性能。我们与其开发人员一起合作,重写了他们现有的基于 OpenMP 的代码,以使用 C++ 并行算法。图 2 是仅显示了应用程序其中某个重要特性的示例。
图 2 将 Lulesh 从 OpenMP 重构为 ISO C++ 并行,可以得到更简单、更易于阅读、符合 ISO 标准并且可移植到所有支持 ISO C++ 编译器的代码
左边的代码使用 OpenMP 跨 CPU 线程并行代码中的循环。为了同时维护代码的串行和并行版本,开发人员使用了 #ifdef 宏和编译器编译指令 pragma。导致的结果是会出现重复代码,并且在源代码中引入了额外的 API,OpenMP。
右边的代码是相同的例程,但是使用 C++ transform_reduce 算法重写了。由此生成的代码更紧凑、更不容易出错、更易于阅读且更易于维护。它还移除了对 OpenMP 的依赖项,转而依赖于 C++ 标准模板库,同时为所有平台维护了单一的源代码。该代码完全符合 ISO C++,能够由任何支持 C++ 17 的 C++ 编译器构建。事实证明,它也更快了!
图 3 Lulesh 的 ISO C++ 版本比原始的 OpenMP 代码更快,可移植到多个编译器,并且可在 CPU 和 GPU 之间进行移植
作为性能的基准,我们使用的 OpenMP 代码用 GCC 构建并运行在 AMD EPYC 7742 处理器的所有内核上。使用 NVIDIAnvc++编译器重新构建该基线代码,在 CPU 上基本可以获得相同的性能。
如果你使用同一版本的 GCC 来构建 ISO C++ 代码,并让其运行在相同的 CPU 上,那么性能将提高大约 50%,这是由于编译器各种改进开销,使其有机会更好地优化代码。
当使用 nvc++构建代码并在同一个 CPU 上运行时,性能能够提高 2 倍。这已经是一项激动人心的成就了,但除此之外,你可以构建相同的代码,只需将编译器选项更改为针对 NVIDIA GPU,而不是多核 CPU 的。现在,同样的代码在 NVIDIA A100 GPU 上运行,速度快了 13 倍多。与原始代码相比,使用严格的 ISO C++ 的代码,性能提高了 13.5 倍,并且能在 CPU 和 GPU 上并行运行。
STLBM
应用 C++ 标准并行的另一个例子是 STLBM,来自日内瓦大学的 Lattice-Boltzmann 求解器。Jonas Latt 教授在多个 GTC 会议中讨论了这个应用,展示了在没有依赖任何外部 SDK 的情况下,用 ISO C++ 编写的代码如何在多个编译器以及多个硬件平台上运行的,包括 NVIDIA GPU。相关的更多信息,请参见 《GPU 上的流体力学与 C++ 并行算法:通过硬件无关的方式获取最先进的性能》 和《使用 C++ 标准并行将一个科学应用移植到 GPU 上》
他的应用程序使用 GPU 实现了超过 12 倍的性能提升。值得注意的是,他的比较基准是一个默认情况下是并行的源代码,使用 C++ 17 标准模板库中的并行算法来表达应用程序中固有的并行性。
他将使用 ISO C++ 作为 GPU 编程的经验归类为“跨平台 CPU/GPU 编程的范式转换”。他的团队没有编写一个默认情况下是串行的应用程序,然后再添加并行性,而是编写了一个可以在任何他们希望的并行平台上运行的应用程序。
图 4 STLBM 能够在多核 CPU 节点和 NVIDIA GPU 上运行相同的源代码
英伟达在 C++ 并行性和并发性的持续开发方面投入了大量的资金,并为即将到来的 C++ 23 规范共同撰写了各种提案,以进一步提高编写“并行优先”(parallel-first)代码的能力。
ISO Fortran
Fortran 仍然是一种主要关注科学和高性能计算的语言。Fortran 最初是公式转换器(FORmula TRANslator),它为开发人员和编译器提供了各种优势,并且还拥有用于建模和仿真代码的庞大现有代码库。
Fortran 开始在 Fortran 2008 中添加支持并行编程的特性,在 Fortran 2018 中增强了这些能力,并在即将推出的版本(目前称为 Fortran 202X)中继续完善它们。与 ISO C++ 一样,英伟达一直在与其应用程序开发人员合作,在 Fortran 中使用标准语言的并行来现代化他们的应用程序,使其成为“并行优先”(parallel-first)。
计算化学
我的同事 Jeff Hammond 在他的 “FortranCon2021:GPU 上的标准 Fortran 及其在量子化学代码中的应用” 会议介绍中,展示了一些使用 Fortran 在内核中执行do concurrent循环的有前景的结果,这些循环取自 NWChem 应用程序,以及另一个计算化学应用程序 GAMESS。
对于 NWChem,他隔离了多个执行张量收缩的性能关键循环,并使用多个编程模型来编写它们。在多核 CPU 上,这些张量收缩使用 OpenMP 来跨 CPU 内核进行线程处理。对于 GPU 来说,有一些版本可以使用 OpenACC、OpenMP 目标卸载,现在还可以使用 Fortran do concurrent 循环。
图 5 显示,在 NVIDIA GPU 上执行do concurrent循环的性能与 OpenACC 和 OpenMP 目标卸载的性能相同,但不需要在应用程序中包含这些额外的 API。这都是标准的 Fortran。
图 5 使用多种编程模型内核的一系列 NWChem 应用程序的性能
高性能通量传输
在最近与 SC21 会议同期举行的指令加速器编程研讨会(WACCPD)上,来自 Predictive Science Inc. 的一个开发团队展示了他们其中的一个重构代码的结果,这些代码之前使用 OpenACC 在 NVIDIA GPU 上运行,使用了 do concurrent 循环。
他们比较了使用 NVIDIA nvfortran 、 gfortran 和 ifort 构建这个纯 ISO Fortran 应用程序的结果。他们得出的结论是,对于使用 nvfortran 编译器的应用程序,纯 Fortran 无需任何指令即可提供他们所需的性能。此外,该代码无需修改即可在 GPU 和多核 CPU 上并行运行。
这图 6 使用 nvfortran 编译器的 HipFT 基准测试的性能结果
这篇论文获得了研讨会的最佳论文奖,尽管它根本不需要任何加速器编程指令。当被问及是否会在他们的其他应用程序中继续采用标准语言并行方式时,演讲者回答说,他们已经计划在公司的其他重要应用程序中采用这种方式。
带有Legate和cuNumeric的Python
在过去的十年中,Python 语言的受欢迎程度急剧上升。它现在常用于机器学习、数据科学,甚至传统的建模和仿真应用。虽然 Python 与 C++ 和 Fortran 不同,它不是 ISO 标准的编程语言,但我们也在 Python 语言中实现了标准语言并行的精神。
在秋季 GTC’21 的主题演讲中,英伟达首席执行官黄仁勋(Jensen Huang)介绍了 cuNumeric 的 alpha 版本,这是一个以 NumPy 为模型的库,它能够实现我前面讨论的 ISO C++ 和 Fortran 类似的特性。NumPy 包在 Python 开发中非常普遍,几乎可以肯定,任何用 Python 编写的 HPC 应用程序都会使用它。
cuNumeric包编写在一个名为 Legate 的包之上,它使 NumPy 应用程序不仅能够自动将工作扩展到 GPU 上,而且还能在跨大型集群中的 GPU 上自动扩展工作。我已经看到了几个示例应用程序,它们只是简单地将代码中对 NumPy 的引用替换为对 cuNumeric 的引用,就可以将该应用程序稍微地扩展到 NVIDIA 内部集群 Selene 的完整大小,Selene 是世界上最快的 10 台超级计算机之一。
有关 cuNumeric 的更多信息,请参阅英伟达宣布推出的 cuNumeric Public Alpha 版 ,并观看 GTC 点播会议,Legate:扩展 Python 生态系统。
结 论
我希望这篇文章能够给你启发,使你明白 GPU 编程并不像你听说的那么难。如果你使用标准语言并行,甚至可能不需要做任何代码更改。
英伟达鼓励你以并行优先的方式编写应用程序,这样就永远不需要将应用程序“移植”到新平台,而标准语言并行是实现这一点的最佳方式,因为它只需要 ISO 标准语言。这就是为什么我们继续投资于 ISO 编程语言,并为这些语言带来更多的并行和并发特性的原因。
总之,使用标准语言并行有以下好处:
- 完全符合 ISO 语言标准,代码更易于移植
- 代码更紧凑、更容易阅读、更不容易出错
- 默认情况下代码是并行的,因此可以在更多平台上运行而无需修改
以下是来自 GTC’21 的一些演讲,它们可以为你提供更多关于这种并行编程方式的细节:
相关更多信息,请参阅以下资源:
- 了解有关编译器支持的更多信息以及 HPC SDK 上的其他文章。
- 免费下载 HPC SDK 软件。
作者介绍
Jeff Larkin 是英伟达高性能计算(HPC)软件团队的首席 HPC 应用架构师。他对高性能计算并行编程模型的发展和采用充满热情。他曾是英伟达开发技术小组的成员,专门从事高性能计算应用的性能分析和优化。Jeff 还是 OpenACC 技术委员会的主席,他曾在 OpenACC 和 OpenMP 标准机构工作过。在加入英伟达之前,Jeff 曾在位于橡树岭国家实验室(Oak Ridge National Laboratory)的 Cray 超级计算卓越中心工作过。
本文转自 公众号:AI前线 ,作者Jeff Larkin,点击阅读原文