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)

    • 自动向量化:NVIDIA, GCC, LLVM, ACI, Cray...
    • 编译器指令:例如 OpenMP
      • #pragma omp parallel for simd
      • #pragma vector always
  2. 库 (Libraries)

    • NVIDIA Math Libraries (NVPL)
    • Arm Performance Library (ArmPL)
    • 开源科学库 (BUS, FFTW, PETSc, etc.)
    • 库的类别包括:BLAS, LAPACK, PBLAS, SCALAPACK, TENSOR, SPARSE, RAND, FFTW
  3. 内联函数 (Intrinsics - ACLE)

  4. 汇编 (Assembly)

编译器自动向量化 (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]