编译器#

许多商业和开源编译器完全支持 NVIDIA Grace。本节提供有关可用编译器、推荐版本和推荐命令行选项的信息。

NVIDIA HPC 编译器#

NVIDIA HPC SDK 包括经过验证的编译器、库和软件工具。HPC SDK 编译器 (NVHPC) 支持为 NVIDIA GPU 和多核 Arm、OpenPOWER 或 x86-64 CPU 进行跨平台 C、C++ 和 Fortran 编程。这些编译器非常适合使用 OpenMP、OpenACC 和 NVIDIA CUDA® 以 C、C++ 或 Fortran 编写的 HPC 建模和仿真应用程序。

在 Grace 上本地构建时,NVHPC 23.3 或更高版本会自动针对 Grace 进行优化,而无需额外的命令行选项。要验证,请传递 --version 命令行选项并查找输出中是否包含 -tp neoverse-v2

nvidia@localhost:~$ nvc --version
nvc 23.3-0 linuxarm64 target on aarch64 Linux -tp neoverse-v2
NVIDIA Compilers and Tools
Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.

NVHPC 的优化和浮点控制标志在 NVIDIA Grace 上与其他 CPU 上相同。有关更多信息,请参阅《NVIDIA HPC 编译器用户指南》。

注意

NVIDIA 提供了针对 Grace 优化的 BLAS、LAPACK 和 FFT 数学库,我们强烈建议您使用它们。

GNU 工具链#

当使用 GNU 工具链时,我们建议使用 GCC 12.3 或更高版本。GCC 版本 7 可以在 NVIDIA Grace 上使用,但由于这些旧版本的编译器针对的是早期的 Armv8-A 架构变体,因此性能将是次优的。GNU 工具链的最新和最佳二进制版本可以从您的 GNU/Linux 发行版中找到,也可以使用 Spack 下载。

注意

如果可能,请始终使用最新版本的 GCC。

即使使用最新版本的 GCC,除非您的工具链已针对 Grace 进行配置和构建,否则仍需要额外的命令行选项才能为 NVIDIA Grace 生成最佳代码。如果未提供其他标志,GCC 将生成针对通用 Armv8-A CPU 的代码。下表提供了推荐的标志。

注意

更激进的优化将以浮点精度换取性能。

组织级别和标志#

优化级别

标志

注释

激进

-Ofast -mcpu=neoverse-v2

启用快速数学优化

中等

-O3 -mcpu=neoverse-v2

在大多数情况下推荐

在所有情况下都使用 -mcpu=neoverse-v2 标志。我们建议您使用 -mcpu 标志而不是 -march-mtune 标志,因为 -mcpu 标志将选择您针对的 CPU,以便于使用,而不是使用 -march 选项指定具有所需扩展的架构,然后指定 -mtune 选项。有关可以按功能打开和关闭的指令集功能的更多信息,请参阅 https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html#aarch64-feature-modifiers

GNU C 或 GNU C++ 中的 __sync 内置函数是 C11 / C++11 标准中使用的现代原子扩展的前身。这些内置函数现在被认为是遗留的,用户应该移植 C11 / C++11 中的原子扩展。这对任何平台都是一个很好的建议,但对于实现 AArch64 架构的 CPU 尤其重要,因为遗留的 __sync 同步内置函数倾向于强制执行比必要更严格的排序。有关更多信息,请参阅 https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html

C 标准未指定 char 类型的有符号性。在 x86 上,char 默认假定为有符号,而在 Arm 上,char 假定为无符号。当数字的符号很重要时(例如,uint8_tint8_t),或者通过使用 -fsigned-char 标志在编译时设置 char 的有符号性,可以使用指定有符号性的标准 int 类型来解决此差异。

有关 GCC 中 AArch64 目标所需的命令行选项的更多信息,请参阅 https://gcc.gnu.org/onlinedocs/gcc-13.1.0/gcc/AArch64-Options.html

LLVM Clang 和 Flang 编译器#

当您使用 LLVM 时,我们建议使用 16 或更高版本。LLVM 编译器支持 Arm64 CPU,但主要用于 C 和 C++(clang 和 clang++ 命令)。LLVM 的 Fortran 编译器 (flang) 尚未广泛使用,并且仍在成熟中。与 GNU 编译器一样,Clang 优先考虑可移植性而不是性能,并且为了启用优化,必须添加额外的标志。

NVIDIA 在 developer.nvidia.com/grace/clang 提供了专门为 Grace CPU 打包的 LLVM Clang 构建版本。这些构建版本是配置为支持 Grace 的主线 Clang,因此这些构建版本可以用作您当前工作流程中 Clang 的直接替代品。下表提供了有关优化级别和标志的信息。

优化级别和标志#

优化级别

标志

注释

激进

-Ofast -mcpu=neoverse-v2

启用快速数学优化。

中等

-O3 -mcpu=neoverse-v2

在大多数情况下推荐。

保守

-O3 -ffp-contract=off -mcpu=neoverse-v2

禁用融合数学运算。

在所有情况下都使用 -mcpu=neoverse-v2 标志,我们建议使用 -mcpu 标志而不是 -march-mtune 标志。

Arm Linux 编译器和其他商业编译器#

Arm Linux 编译器 (ACfL) 是 Arm 提供的一款商业支持的闭源编译器。它是免费的,并捆绑了优化的 BLAS、LAPACK 和 FFT 库。Arm 通过支持 Neoverse-V2 CPU 微架构在 ACfL 中支持 NVIDIA Grace。有关编译器选项、标志和支持的更多信息,请参阅 Arm Linux 编译器

系统供应商,例如 HPE/Cray 和 Fujitsu,也提供针对其自己的基于 Arm 的产品的编译器。这些供应商编译器生成的代码往往针对目标平台进行了高度调优,这使其成为对性能至关重要的场景中的一个不错的选择。请联系系统供应商以获取信息和支持。

Arm 架构特性支持#

与其他主要 CPU 架构一样,有些指令无法通过直接翻译 C/C++ 来访问。这些指令通常由编译器内联函数和一组功能宏来支持,这些功能宏可以在应用程序中使用,以在编译时测试特定架构扩展的支持。编译器 Arm 架构的常用内联函数集、附加数据类型、架构功能宏等由 Arm C / C++ 语言扩展 (ACLE) 定义。有关更多信息,请参阅 ARM-software/acle

关于 ACLE 和内联函数编程的讨论超出了本文档的范围,用户应参考本文档及其编译器文档,以了解合规性级别。

NVIDIA Grace 实现了 Armv9-A 架构和几个 Armv9-A 架构扩展。要查看在编译时启用了哪些 Arm 架构功能,请运行以下命令

gcc -dM -E -mcpu=neoverse-v2 - < /dev/null | grep ARM_FEATURE

这是在 NVIDIA Grace 上使用 GCC 12 的输出示例

nvidia@localhost:~$ gcc -dM -E -mcpu=native - < /dev/null | grep ARM_FEATURE | sort
#define __ARM_FEATURE_AES 1
#define __ARM_FEATURE_ATOMICS 1
#define __ARM_FEATURE_BF16_SCALAR_ARITHMETIC 1
#define __ARM_FEATURE_BF16_VECTOR_ARITHMETIC 1
#define __ARM_FEATURE_CLZ 1
#define __ARM_FEATURE_COMPLEX 1
#define __ARM_FEATURE_CRC32 1
#define __ARM_FEATURE_CRYPTO 1
#define __ARM_FEATURE_FMA 1
#define __ARM_FEATURE_FP16 FML 1
#define __ARM_FEATURE_FP16_SCALAR_ARITHMETIC 1
#define __ARM_FEATURE_FP16_VECTOR_ARITHMETIC 1
#define __ARM_FEATURE_FRINT 1
#define __ARM_FEATURE_IDIV 1
#define __ARM_FEATURE_JCVT 1
#define __ARM_FEATURE_MATMUL_INT8 1
#define __ARM_FEATURE_NUMERIC_MAXMIN 1
#define __ARM_FEATURE_QRDMX 1
#define __ARM_FEATURE_SHA2 1
#define __ARM_FEATURE_SHA3 1
#define __ARM_FEATURE_SHA512 1
#define __ARM_FEATURE_SM3 1
#define __ARM_FEATURE_SM4 1
#define __ARM_FEATURE_SVE 1
#define __ARM_FEATURE_SVE2 1
#define __ARM_FEATURE_SVE2_AES 1
#define __ARM_FEATURE_SVE2_BITPERM 1
#define __ARM_FEATURE_SVE2_SHA3 1
#define __ARM_FEATURE_SVE2_SM4 1
#define __ARM_FEATURE_SVE_BITS 0
#define __ARM_FEATURE_SVE_MATMUL_INT8 1
#define __ARM_FEATURE_SVE_VECTOR_OPERATORS 1
#define __ARM_FEATURE_UNALIGNED 1

有关 arm64 上目标特定标志的更多信息,请参阅以下命令的输出

gcc -Q --help=target

这是示例输出

 nvidia@localhost:~$ gcc -Q --help=target
 The following options are target specific:
 -mabi= lp64
 -march= armv8-a
 -mbig-endian                        [disabled]
 -mbionic                            [disabled]
 -mbranch-protection=
 -mcmodel= small
 -mcpu= generic
 -mfix-cortex-a53-835769             [enabled]
 -mfix-cortex-a53-843419             [enabled]
 -mgeneral-regs-only                 [disabled]
 -mglibc                             [enabled]
 -mharden-sls=
 -mlittle-endian                     [enabled]
 -mlow-precision-div                 [disabled]
 -mlow-precision-recip-sqrt          [disabled]
 -mlow-precision-sqrt                [disabled]
 -mmusl                              [disabled]
 -momit-leaf-frame-pointer           [enabled]
 -moutline-atomics                   [enabled]
 -moverride=<string>
 -mpc-relative-literal-loads         [enabled]
 -msign-return-address= none
 -mstack-protector-guard-offset=
 -mstack-protector-guard-reg=
 -mstack-protector-guard= global
 -mstrict-align [disabled]
 -msve-vector-bits=<number> scalable
 -mtls-dialect= desc
 -mtls-size= 24
 -mtrack-speculation                 [disabled]
 -mtune= generic
 -muclibc                            [disabled]
 -mverbose-cost-dump                 [disabled]

- Known AArch64 ABIs (for use with the ``-mabi=`` option): ilp32 lp64
- Supported AArch64 return address signing scope (for use with the ``-msign-return-address=`` option): all non-leaf none
- The code model option names for ``-mcmodel``: large small tiny
- Valid arguments to ``-mstack-protector-guard=``: global sysreg
- The possible SVE vector lengths: 1024 128 2048 256 512 scalable
- The possible TLS dialects: desc trad

使用代码局部性提高性能#

提高可执行代码的局部性可以提高 Grace 的效率,这有利于指令缓存命中率、iTLB 命中率和分支预测。代码分布在较宽虚拟地址范围内的可执行文件和大型共享对象可能会通过将频繁调用的函数分组到尽可能少的自然对齐的 2 MB 虚拟地址范围内来看到性能提升。perf recordperf script 命令可以帮助确定一段时间内观察到的程序计数器地址。要确定应用程序是否可能是此优化的候选者,请计算 perf 输出中观察到的地址范围的数量。

对于将在快速连续访问超过 30 个范围的大型应用程序和/或库,这种优化类型可能会产生高达 50% 的加速。为了实现这一点,有几种方法可以重新排列链接的二进制文件/二进制文件,以将频繁调用的函数分组或将函数与其通常调用的其他函数分组。例如,某些形式的自动化配置文件引导优化 (PGO) 在这种情况下可能是有益的。perf record/perf script 输出也可以用于捕获最频繁调用的函数的名称。通过使用 -ffunction-sections 进行编译,可以将观察到的函数名称的频率排序列表用于生成链接器脚本,该脚本将“热”函数在内存中附近分组,从而实现相同的目标。

NVIDIA/cpu-code-locality-tool 的脚本可以帮助自动化分析 perf record 输出的过程,以识别优化的候选对象,并在适用的情况下生成上述链接器脚本。

通常,减小代码大小的优化可能是有益的,因为较小的代码自然跨越较少的 2 MB 范围。例如,如果您正在使用 gcc -O3,请考虑使用 -fno-ipa-cp-clone