Application Optimization for NVIDIA Grace CPU

Lukas Krenz, Mathias Wagner, Sr. Developer Technology Engineer
| S72978 | GTC 2025

目录

  1. NVIDIA Grace CPU 基础
  2. 让您的工作负载运行起来
  3. 实现最佳性能
  4. 编译器
  5. 选择优化的数学库
  6. 性能分析
  7. 理解硬件:Grace 文档
  8. 硬件性能计数器
  9. 工具概览
  10. Perf stat
  11. Nsight Systems
  12. NVTX
  13. Nsight Systems - 核心指标
  14. Nsight Systems - Uncore 指标
  15. 识别热点 (Identify Hotspots
  16. Compiler Explorer – 理解你的编译器在做什么
  17. SIMD 编程方法 (SIMD Programming Approaches
  18. 编译器自动向量化 (Compiler Auto-vectorization
  19. Arm 内存模型 (Arm Memory Model
  20. 总结 (Summary

NVIDIA Grace CPU 基础

超级芯片的构建模块

Page 2 - NVIDIA Grace CPU 基础
Page 2 - NVIDIA Grace CPU 基础
  • 高性能高能效核心
    • 72个旗舰级 Arm Neoverse V2 核心,每个核心配备 SVE2 4x128b SIMD。
  • 快速片上互联结构
    • 3.2 TB/s 的对分带宽连接 CPU 核心、NVLink-C2C、内存和系统 IO。
  • 高带宽低功耗内存
    • 高达 480 GB 的数据中心级 LPDDR5X 内存,可在 16W 功率下提供高达 500 GB/s 的内存带宽。
  • 一致性的芯片间连接
    • NVLink-C2C 提供 900 GB/s 的带宽,用于 CPU 或 GPU 之间的一致性连接。
  • 业界领先的每瓦性能
    • 相较于当今领先的服务器,每瓦性能提升高达 2 倍。

让您的工作负载运行起来

Page 3 - 工作负载运行流程
Page 3 - 工作负载运行流程
  • 期望:它“就是能用”,Arm 生态系统非常成熟。
  • 大多数应用程序/库在 Grace 上都可以开箱即用。
  • 首先关注正确性
  • 性能优化是一个迭代的过程。

实现最佳性能

复用他人的工作

Page 4 - 性能优化步骤
Page 4 - 性能优化步骤
  • 首先把基础工作做好
    • 使用正确的编译器和正确的编译器标志。
    • 启用编译器优化。
    • 如果适用,使用优化库。
  • 仅当上述步骤失败时
    • 为您的应用程序收集数据。
    • 找出性能瓶颈。
    • 改进代码。
  • 首先进行高级别改进(例如 C++)。
  • 然后进行低级别改进(内联函数、汇编)。
  • 这些步骤中的每一步都应涉及验证正确性,并且应由数据驱动

编译器

Page 5 - 编译器及版本要求
Page 5 - 编译器及版本要求
  • 使用支持 Grace/Neoverse V2 的编译器
  • 检查并更新您的编译器标志
  • 使用标志 -O3 -mcpu=native -ffp-contract=fast
    • 当使用 native 时,GCC 的 -mcpu=neoverse-v2+crypto+sha3+sm4+sve2-aes+sve2-sha3+sve2-sm4 标志会启用所有功能(可能不检测加密扩展)。
    • 如果可以接受快速数学优化,请使用 -Ofast,但在移植前请务必检查其准确性。
    • 如果输出的一致性很重要,请使用 -ffp-contract=off 来禁用浮点运算收缩(例如,FMA)。
  • 使用 -flto 来启用链接时优化
  • Grace 受益于代码局部性:考虑使用配置文件引导优化(Profile-Guided Optimization)。
  • 应用程序可能需要 -fsigned-char-funsigned-char,具体取决于开发者的假设。
  • Fortran 可能会从 -fno-stack-arrays 中受益。
  • 可以从 https://developer.nvidia.com/grace/clang 获取 NVIDIA 构建的 Clang:为速度而优化,并经过验证与核心 NVIDIA 技术(如 CUDA® Toolkit)兼容。

选择优化的数学库

使用默认接口以实现性能可移植性

Page 6 - 优化数学库性能对比
Page 6 - 优化数学库性能对比

性能分析

Page 7 - 性能分析循环
Page 7 - 性能分析循环
  • 了解您的硬件
    • 对您的代码重要的硬件能力。
  • 了解您的代码
    • 所用算法的主要特性。
  • 了解您的工具
    • 使用各种工具(NVIDIA, Arm, 第三方, 开源...)。
    • 不同的工具有不同的优势。
    • 构建一套工具来满足您的需求。
  • 获得一个可靠的基线性能(可复现)
  • 为性能预期建立模型
  • 对于大型应用:找到一个合适的代理(proxy)
    • 需要相当快的周转时间。
    • 可能需要使用工具进行多轮分析。

理解硬件:Grace 文档

Page 8 - Grace 相关文档
Page 8 - Grace 相关文档

硬件性能计数器

  • Grace 通过其性能监控单元(Performance Monitoring Units, PMU)支持多个性能计数器
  • 核心事件:测量每个核心内发生的所有事情,如指令、周期、分支、分支未命中、缓存未命中。
  • Uncore 事件:涵盖核心之外发生的所有事情,在套接字(socket)级别收集。
    • 可扩展一致性结构(Scalable Coherence Fabric)事件:涵盖末级缓存、内存流量以及套接字之间的流量。
    • PCIe 事件:涵盖通过 PCIe 的流量,例如在使用 NVMe 磁盘时用于监控 I/O。
  • Arm 统计性能分析扩展(Arm Statistical Profiling Extension, SPE)
    • 独立的测量单元——不属于核心 PMU。
    • 在 CPU 后端进行采样。
    • 收集精确的程序计数器(指令指针)值(无偏差)、数据地址、延迟等信息。
    • 目前工具支持有限。
  • 更多详情请参考 NVIDIA Grace 调优指南和 Arm Neoverse V2 PMU 指南 (https://developer.arm.com/documentation/109709/latest/)

工具概览

  • 许多性能工具都支持 Grace
  • 理想情况下,应结合使用多种工具,每种工具都有其优势和劣势。
  • NVIDIA 开发了 NVIDIA Nsight Systems
    • 提供带有硬件事件和指标的时间线视图。
    • 支持代码注释。
  • 开源工具,例如
    • Perf:收集硬件计数器、调用栈等等。
    • Likwid:硬件性能计数器,支持对区域进行注释。
    • eBPF 工具,如 bpftrace:内核追踪、用户级追踪、跟踪点等。
  • Linaro MAP:专注于并行代码的商业性能分析器。

Perf stat

  • 用于计算事件的工具
  • 在不计算过多事件时开销极低
    • → 可用作运行基准测试时的默认包装器。
  • 测量平均频率(周期/时间)、IPC(指令/周期)、分支未命中等
  • 提供对基准测试和 CPU 行为的初步了解
  • 高级用法:计算事件的时间序列,例如随时间变化的内存带宽。
  • 示例
$ perf stat -I 100 -o perf.json --json \
-e 'duration_time,{nvidia_scf_pmu_0/cmem_rd_data/,nvidia_scf_pmu_0/cmem_wr_total_bytes/}'
  • "以 JSON 格式 (-o perf.json --json) 每 100ms (-I 100) 收集事件 duration_time, ... (-e ...)"
  • 根据指标计算 socket 0 上的内存流量(GB/s)(32 * cmem_rd_data + cmem_wr_total_bytes) / duration_time / 1e9

Nsight Systems

时间线视图

Page 12 - Nsight Systems 时间线视图
Page 12 - Nsight Systems 时间线视图

NVTX

标记您的代码

Page 13 - 使用 NVTX 标记代码
Page 13 - 使用 NVTX 标记代码
#include <nvtx3/nvToolsExt.h>

void congrad_64()
{
    nvtxRangePush(__func__); // Range around the whole function

    for (int i = 0; i < 6; ++i)
    {
        nvtxRangePush("loop range"); // Range for iteration
        // Do ab iteration
        nvtxRangePop(); // End the inner range
    }

    nvtxRangePop(); // End the outer range
}

Nsight Systems - 核心指标

收集核心性能指标

Page 14 - Nsight Systems 核心指标视图
Page 14 - Nsight Systems 核心指标视图

使用以下命令查看可用的核心指标:
nsys profile --cpu-core-metrics=help

Nsight Systems - Uncore 指标

收集 uncore 性能指标

Page 15 - Nsight Systems Uncore 指标视图
Page 15 - Nsight Systems Uncore 指标视图

使用以下命令查看可用的套接字(socket)级别指标:
nsys profile --cpu-socket-metrics=help

识别热点 (Identify Hotspots)

链接:https://github.com/brendangregg/FlameGraph

  • 使用 Nsight Systems
    - 在 Nsight Systems 安装目录下的 Scripts/Flamegraph 目录中
    - nsys profile -o report ./app
    - python3 stackcollapse_nsys.py report.nsys-rep | ./flamegraph.pl > result_flamegraph.svg

  • 使用 perf
    - perf record -a -g ./app
    - perf script | stackcollapse-perf.pl > out.perf-folded
    - flamegraph.pl out.perf-folded > perf.svg

火焰图示例
火焰图示例

Compiler Explorer – 理解你的编译器在做什么

链接:https://godbolt.org

该工具可以帮助开发者理解编译器对源代码所做的具体优化和转换,通过并排显示源代码、生成的汇编代码以及编译器优化后的代码,可以直观地看到向量化等优化的效果。

Compiler Explorer 界面展示,代码来自 HACCmk 基准测试
代码来自 Coral 基准测试套件的 HACCmk 基准测试,来源:https://asc.llnl.gov/coral-benchmarks

SIMD 编程方法 (SIMD Programming Approaches)

请按顺序遵循这些建议,例如,优先选择自动向量化而不是内联函数(intrinsics)。

SIMD编程方法层级图
SIMD编程方法层级图
  1. 编译器 (Compilers)
  2. 自动向量化:NVIDIA, GCC, LLVM, ACI, Cray...
  3. 编译器指令:例如 OpenMP
    - #pragma omp parallel for simd
    - #pragma vector always

  4. 库 (Libraries)
    - NVIDIA Math Libraries (NVPL)
    - Arm Performance Library (ArmPL)
    - 开源科学库 (BUS, FFTW, PETSc, etc.)

  5. 库的类别包括:BLAS, LAPACK, PBLAS, SCALAPACK, TENSOR, SPARSE, RAND, FFTW

  6. 内联函数 (Intrinsics - ACLE)
    - https://developer.arm.com/architectures/instruction-sets/intrinsics
    - https://developer.arm.com/documentation/100987/latest
    - https://arxiv.org/abs/1902.09544

  7. 汇编 (Assembly)
    - 参考 https://developer.arm.com/documentation/ddi0584/latest (Arm®v8-A 架构的可扩展向量扩展)

编译器自动向量化 (Compiler Auto-vectorization)

  • 通常在较高的优化级别下启用。
    - 使用一些更激进的选项,如 -Ofast (例如,归约操作)。

  • 诊断标志可以让你看到编译器做了什么以及没有向量化的部分。
    - LLVM:
    - -Rpass(-missed|-analysis)=loop-vectorize
    - GCC:
    - -fopt-info-vec-(optimized|missed|all|note)
    - NVIDIA compilers:
    - -Minfo=vect

  • 查看输出和生成的代码 → 使用 Compiler explorer / Godbolt。

  • 在适用的情况下,在 C / C++ 中使用 restrict 指针。

  • 使用 pragmas 来指导编译器(取决于所使用的编译器)。
    - #pragma GCC ivdep
    - #pragma clang loop vectorize
    - #pragma omp simd

Arm 内存模型 (Arm Memory Model)

  • Arm 的内存模型是弱序 (weakly-ordered) 的,与 x86 的内存模型不同:加载/存储操作可能在运行时被 CPU 重排。

  • 写入操作对其他线程的可见顺序可能不同。

  • 许多程序使用顺序一致性 (sequential consistency) 来在线程间传递消息 (例如, std::atomic with std::memory_order_seq_cst)。

  • 对于线程间的同步,release/acquire 语义可能就足够了。

  • 使用较弱的内存顺序可以提高性能,但也可能引入错误。

  • 如果不确定:请使用库中的同步原语。

  • 这是一个复杂的话题,更多上下文请参考 Herb Sutter 的文章:https://herbsutter.com/2013/02/11/atomic-weapons-the-c-memory-model-and-modern-hardware/

总结 (Summary)

主要 takeaways

  • 大多数应用程序将能开箱即用 (out of the box)
  • 正确性第一,性能第二
  • 首先把基础工作做好
  • 选择一个推荐的编译器。
  • 使用最佳的标志进行编译。
  • 如果可能,使用性能库。
  • 使用数据驱动的迭代优化工作流
  • 使用性能分析工具
    - NVIDIA Nsight Systems
  • 开源工具,如 perf
  • 理解你的性能预期和瓶颈。
  • 优化你的热点,但前提是存在优化的潜力。

也欢迎访问我们今天下午 3:00 – 3:50 PM 的 CWE:
如何在 NVIDIA Grace CPU 上运行和优化您的工作负载 [CWE73338]