GPU基础:GPU架构、CUDA/ROCm基础与GPU虚拟化技术概述
核心原理:GPU的并行计算架构
GPU(图形处理器)最初为图形渲染设计,其本质是一个大规模并行计算引擎。与CPU追求低延迟、复杂控制逻辑不同,GPU将大量晶体管用于算术逻辑单元(ALU)和寄存器,采用“单指令多线程”(SIMT)的执行模型。理解GPU架构是掌握容器化GPU资源分配的前提,因为资源隔离的粒度、性能损耗的根源都源于硬件结构。
以NVIDIA Ampere架构为例,其硬件层级自上而下为:
- GPU芯片(Die):包含多个图形处理簇(GPC),每个GPC内含多个流式多处理器(SM)。
- SM(Streaming Multiprocessor):核心计算单元,包含CUDA核心、Tensor Core、共享内存(Shared Memory)、寄存器堆和调度器。
- CUDA核心:执行浮点/整数运算的最小单元,一个SM通常包含128个CUDA核心。
- 显存(VRAM):通过高速总线与SM连接,容量和带宽决定数据吞吐上限。
AMD的CDNA架构(用于Instinct系列)在逻辑上类似,但将计算单元(CU)作为SM的对应物,每个CU包含多个流处理器(Stream Processor)。关键差异在于软件栈:NVIDIA使用CUDA,AMD使用ROCm(Radeon Open Compute)。
CUDA与ROCm:异构计算软件栈
容器化GPU资源分配的核心在于软件栈的隔离与透传。CUDA和ROCm都遵循“运行时库 + 驱动”的分层模型,但实现细节差异显著。
| 层次 | NVIDIA CUDA | AMD ROCm |
|---|---|---|
| 编程语言 | CUDA C/C++(NVCC编译器) | HIP C++(可移植到CUDA) |
| 运行时API | libcudart.so(cudaMalloc, cudaMemcpy) | libamdhip64.so(hipMalloc, hipMemcpy) |
| 驱动接口 | libcuda.so(CUDA Driver API) | libhsa-runtime64.so(HSA Runtime) |
| 内核模块 | nvidia.ko(专有) | amdgpu.ko(开源)+ ROCk |
| 容器支持 | NVIDIA Container Toolkit | ROCm Docker / AMDToolkit |
在容器场景中,宿主机只需安装内核态驱动,而容器内可携带用户态运行时。例如,宿主机安装515.76驱动,容器内可运行CUDA 11.8或12.0的镜像,只要用户态库与内核驱动版本兼容(通常要求驱动版本≥运行时要求的最低版本)。
GPU虚拟化技术:从直通到MIG
GPU虚拟化经历了从“整卡直通”到“细粒度切片”的演进,容器化资源分配正是建立在以下三种技术之上:
- API重定向(API Forwarding):如NVIDIA vGPU的前身,在宿主机拦截CUDA调用并转发到物理GPU。容器内无需物理GPU驱动,但性能损耗约10%-20%。
- 整卡直通(Passthrough):通过PCIe直通将整块GPU分配给单个容器/虚拟机。性能接近裸机,但无法切分,且容器迁移时GPU必须随迁。
- 硬件级切片(MIG / SR-IOV):NVIDIA A100/H100支持多实例GPU(MIG),可将物理GPU划分为最多7个独立实例,每个实例拥有专属SM、显存带宽和L2缓存。AMD的MxGPU(基于SR-IOV)提供类似能力。
案例:容器中分配MIG设备
假设宿主机有一块A100 40GB GPU,需创建两个容器分别获得20GB和10GB的MIG切片。操作流程如下:
# 1. 在宿主机启用MIG并创建实例(需root权限)
nvidia-smi mig -cgi 19,19,9,9,9 -C
# 解释:19代表20GB切片(对应1g.5gb),9代表10GB切片(对应1g.5gb+)
# 2. 查看生成的MIG设备UUID
nvidia-smi -L
# MIG 3g.20gb: GPU-4a2b1c3d...
# 3. 启动容器并指定MIG设备
docker run --rm --gpus '"device=GPU-4a2b1c3d"' \
-v /data:/data nvcr.io/nvidia/cuda:12.0-base \
nvidia-smi -L
# 容器内仅看到该MIG实例,显存20GB,SM数受限
对于ROCm,AMD提供了类似工具:
# AMD GPU拓扑导出与设备选择
rocm-smi --showtopo
export HIP_VISIBLE_DEVICES=0,1 # 容器内只暴露GPU 0和1
误区与复盘:容器内nvidia-smi的“假象”
nvidia-smi 看到显存总量为整卡容量,就认为容器独占了整张GPU。实际上,默认情况下容器共享宿主机的GPU上下文,多个容器可同时向GPU提交任务,显存和SM资源是抢占式共享的。
复盘一个真实故障:某团队在Kubernetes中部署两个推理容器,均未设置显存上限。容器A申请了2GB显存,但实际推理峰值需要6GB;容器B同样未限制。当两者同时运行时,GPU显存溢出(OOM),导致两个容器同时被内核杀死。根因是未使用MIG或CUDA MPS进行资源隔离,仅依赖--gpus参数只能实现“设备可见性”而非“资源配额”。
正确的做法是:
- 使用MIG为每个容器分配固定显存切片(硬件隔离);
- 或使用CUDA MPS设置显存上限(
CUDA_MPS_PIPE_DIRECTORY+CUDA_MPS_ACTIVE_THREAD_PERCENTAGE); - 在Kubernetes中通过
nvidia.com/gpu.memory和nvidia.com/gpu.sm资源字段声明需求(需配合Device Plugin)。
理解GPU架构的层级关系(SM→GPC→芯片)是理解MIG切片粒度的基础——MIG的最小切片单位是1个GPC内的SM子集,而非任意数量的CUDA核心。这解释了为何MIG实例的规格是离散的(如1g.5gb、2g.10gb、3g.20gb),而非连续可调。