解密NVIDIA硬件一致性平台的内存管理:NUMA vs. CDMM

引言

对于在NVIDIA硬件一致性平台(如GH200、GB200、GB300)上进行开发的开发者和集群管理员来说,理解非统一内存访问(NUMA)架构及其对系统性能的影响至关重要。当应用程序没有完全实现NUMA感知时,其性能可能会变得不稳定和不可预测。为了应对这些挑战,NVIDIA推出了**相干驱动程序内存管理(Coherent Driver-based Memory Management, CDMM)**模式,它为NVIDIA驱动程序提供了一种全新的内存管理方式。

本文将深入探讨NUMA和CDMM模式之间的差异,分析它们如何影响应用程序性能,并提供在不同场景下选择合适模式的实用指南。我们将通过代码示例,帮助您更直观地理解这些复杂的内存管理概念。

什么是硬件一致性平台?

NVIDIA的GH200、GB200和GB300等先进系统,通过**NVLink芯片到芯片(C2C)**的直接连接,实现了CPU与GPU之间的硬件内存一致性。这意味着CPU和GPU可以直接寻址对方的内存,从而极大地提升了数据交换效率。然而,这种强大的能力也带来了一些需要注意的副作用,尤其是在传统的NUMA内存管理模式下。

在这里插入图片描述

图1:NVIDIA Grace Hopper超级芯片,一个典型的硬件一致性平台

NUMA模式:机遇与挑战

NUMA模式是硬件一致性平台上的默认设置。在这种模式下,操作系统(OS)会统一管理CPU(主机)和GPU(设备)的内存,将它们视为一个大的内存池。这使得标准的Linux API(如malloc)和CUDA API(如cudaMallocManaged)都可以在CPU和GPU上分配内存。

核心优势

  • 统一内存池:CPU和GPU内存被整合成一个大的地址空间,简化了编程模型。
  • 动态迁移:内核可以根据需要自动或通过用户API提示(如cudaMemPrefetchAsync)在CPU和GPU之间动态迁移内存页面,以优化资源利用率。

面临的挑战

然而,NUMA模式也带来了一些挑战,尤其是在大规模集群管理(如Kubernetes)和需要精确性能控制的场景中:

  1. 内存意外溢出:由于OS将GPU内存视为通用内存池,可能会将一些非GPU计算任务的数据(如文件缓存)放到GPU内存中,导致宝贵的GPU显存被占用,影响计算性能。
  2. Kubernetes下的问题
    • 内存过度报告:Kubernetes的kubelet可能会错误地将GPU内存计入节点总内存,导致Pod请求的内存超过实际可用的主机内存,引发OOM(Out-of-Memory)错误。
    • 内存限制混淆:为Pod设置的内存限制(memory limit)本意是限制主机内存,但在NUMA模式下会同时限制到GPU内存,这破坏了Pod资源隔离的初衷。
    • 跨NUMA节点访问:默认情况下,一个Pod可以访问所有NUMA节点的内存,这意味着一个容器可能会在它无权访问的GPU上分配内存,破坏了资源隔离性。

CDMM模式:更精细的内存控制

为了解决NUMA模式带来的挑战,NVIDIA引入了CDMM模式。CDMM是一种驱动程序的操作模式,它阻止GPU内存作为NUMA节点暴露给操作系统。取而代之的是,NVIDIA驱动程序直接管理GPU内存,将其与CPU的系统内存完全分离。

CDMM模式下的内存行为

特性 NUMA模式 CDMM模式
内存管理 操作系统管理CPU和GPU内存 操作系统管理CPU内存,NVIDIA驱动管理GPU内存
GPU内存暴露 作为通用内存池暴露给OS 不暴露给OS使用
内存迁移 系统分配的内存在CPU和GPU间动态迁移 系统分配的内存不会迁移到GPU

CDMM对CUDA开发者的影响

在CDMM模式下,最显著的变化是系统分配的内存(通过mallocmmap等OS调用分配)不会被迁移到GPU。尽管GPU仍然可以通过C2C链接访问这部分内存,但数据本身不会物理移动到GPU显存上。

这意味着,即使开发者使用内存迁移提示API,如cudaMemPrefetchAsync,这些操作对于系统分配的内存也不会产生迁移效果。

// --- 示例代码:使用cudaMemPrefetchAsync --- 
// 在CDMM模式下,这段代码中的预取操作对于系统分配的内存不会生效

#include <iostream>
#include <cuda_runtime.h>

// CUDA核函数,用于在GPU上执行
__global__ void vectorAdd(int *a, int *b, int *c, int n) {
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i < n) {
        c[i] = a[i] + b[i];
    }
}

int main() {
    const int n = 1024;
    const size_t bytes = n * sizeof(int);

    int *a, *b, *c;

    // 使用cudaMallocManaged分配统一内存
    // 这部分内存可以被CPU和GPU共同访问
    cudaMallocManaged(&a, bytes);
    cudaMallocManaged(&b, bytes);
    cudaMallocManaged(&c, bytes);

    // 在CPU上初始化数据
    for (int i = 0; i < n; ++i) {
        a[i] = i;
        b[i] = i * 2;
    }

    int device = -1;
    cudaGetDevice(&device);

    // 提示驱动程序将数据a和b预取到GPU
    // 在NUMA模式下,这会触发数据从CPU到GPU的物理迁移
    // 在CDMM模式下,如果这是系统分配的内存,则不会发生迁移
    cudaMemPrefetchAsync(a, bytes, device, NULL);
    cudaMemPrefetchAsync(b, bytes, device, NULL);

    // 在GPU上执行核函数
    vectorAdd<<< (n + 255) / 256, 256 >>>(a, b, c, n);

    // 同步设备以确保核函数执行完毕
    cudaDeviceSynchronize();

    // 释放内存
    cudaFree(a);
    cudaFree(b);
    cudaFree(c);

    return 0;
}

CDMM对系统管理员的影响

在CDMM模式下,虽然numactl等工具仍然能看到代表GPU的NUMA节点,但这些节点不会报告任何可用内存。因此,不应使用numactlmbind等工具来管理GPU内存

对于Kubernetes管理员而言,好消息是从Linux驱动580.65.06版本开始,基于GPU Operator的部署默认启用CDMM模式。这极大地简化了在Kubernetes环境中管理GPU资源的工作。

要手动启用CDMM模式,您需要在加载NVIDIA内核模块时传递一个参数:

# --- 示例代码:启用CDMM模式 ---
# 在加载NVIDIA驱动时设置内核模块参数
# 注意:这通常在系统启动或驱动安装脚本中配置

# 1. 卸载现有驱动(如果已加载)
sudo rmmod nvidia_uvm nvidia_drm nvidia_modeset nvidia

# 2. 加载驱动并启用CDMM模式
# NVreg_CoherentDriverMemoryManagement=1 是关键参数
sudo modprobe nvidia NVreg_CoherentDriverMemoryManagement=1
sudo modprobe nvidia_uvm
sudo modprobe nvidia_drm

如何选择:CDMM vs. NUMA

那么,开发者和管理员应该如何在这两种模式之间做出选择呢?以下是一些关键的决策依据:

  • 应用场景

    • NUMA模式:最适合那些依赖操作系统进行内存管理、并希望利用CPU和GPU组合成更大内存池的传统HPC应用。
    • CDMM模式:对于需要直接、精细控制GPU内存的应用程序(如数据库、大型AI模型训练),以及所有基于Kubernetes的部署,是理想的选择。
  • 内存池化

    • NUMA模式:将CPU和GPU内存聚合,适合需要大内存带宽和容量的工作负载。
    • CDMM模式:GPU内存由驱动程序专门管理,确保了GPU计算数据的专用性,避免了OS的干扰。
  • 内存可见性与测量

    • NUMA模式:可以使用标准Linux工具(如numactl)查看整个系统的内存布局,但难以精确区分GPU的专用内存消耗。
    • CDMM模式:提供了对GPU内存消耗的清晰可见性和细粒度控制,便于性能诊断和优化。

总结

通过理解和策略性地实施CDMM模式,开发者和管理员可以充分释放NVIDIA硬件一致性内存架构的潜力,为GPU加速的工作负载确保最佳的性能和控制力。对于在GH200、GB200或GB300等平台上工作的用户,特别是那些使用Kubernetes进行部署的用户,我们强烈建议您启用并利用CDMM模式,以获得更稳定、可预测且高效的性能表现。


推荐阅读

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐