Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3471551
  • 博文数量: 200
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 7276
  • 用 户 组: 普通用户
  • 注册时间: 2013-01-23 18:56
个人简介

将晦涩难懂的技术讲的通俗易懂

文章分类

全部博文(200)

文章存档

2024年(3)

2023年(9)

2022年(4)

2021年(12)

2020年(8)

2019年(18)

2018年(19)

2017年(9)

2016年(26)

2015年(18)

2014年(54)

2013年(20)

分类: LINUX

2024-06-09 20:05:01

GPU虚拟化技术总结

——lvyilong316

随着AI、加密货币等技术的发展,GPU在市场上一卡难求,这也导致GPU售价非常昂贵,而且供货周期也不稳定。对于有GPU需求的企业用户,不但需要思考GPU卡的选型,同时需要考虑怎样尽可能高效利用GPU资源。为了提高GPU资源利用率,很多人选择对GPU进行虚拟化。下面就对当今的GPU虚拟化技术进行一下总结和介绍。

目前,在GPU虚拟化大类上一般分为三种:软件模拟、直通独占(类似网卡独占、显卡独占)、直通共享(如vGPUMIG)。

全软件模拟(sGPU)

{BANNED}中国第一种,全软件模拟(eg sGPU)。这种方式主要通过软件模拟来完成,类似早期qemu用纯软件模拟网卡一样,主要原理就是在Host操作系统层面上建立一些比较底层的API,让Guest看上去好像就是真的硬件一样。这种方式的优点是比较灵活,而且并不需要有实体GPU,当然没有实体GPU的缺点就很明显了,模拟出来的东西运行比较慢。另外就是这个方式并没有官方研发,因此产品质量肯参差不齐。软件模拟虚拟化就不讲了,因为真实场景太少,做做实验还将就用,几乎没法用在生产环境,毕竟性能损失太多。

GPU直通(pGPU

第二种,直通独占 (pGPU) 。其实严格意义上说,这种方式不能称之为虚拟化。直通是{BANNED}最佳早出现,即技术上{BANNED}最佳简单和成熟的方案。直通主要是利用PCIe Pass-through技术,将物理主机上的整块GPU显卡直通挂载到虚拟机上使用,与市场网卡直通的原理类似,但是这种方式需要主机支持IOMMUVT-dIOVA的地址转换使得直通设备可以在硬件层次直接使用GPAGuest Physical Address)地址。

直通模式的技术方案与其他任何PCI直通没有任何区别。由于GPU的复杂性和安全隔离的要求,GPU直通技术相对于任何其他设备来说,会有额外的PCI 配置空间模拟和MMIO的拦截(参见QEMU VFIO quirk机制)。比如Hypervisor或者Device Module 不会允许虚拟机对GPU硬件关键寄存器的完全的访问权限,一些高权限的操作会被直接拦截。大家或许已经意识到原来直通设备也是有MMIO模拟和拦截的。这对于我们理解GPU 半虚拟化很有帮助。

PCI 直通的技术实现:所有直通设备的PCI 配置空间都是模拟的。而且基本上都只模拟256 Bytes的传统PCI设备,很少有模拟PCIE设备整个4KB大小的。而对PCI设备的PCI bars则绝大部分被mmapqemu进程空间,并在虚拟机首次访问设备PCI bars的时候建立EPT 页表映射,从而保证了设备访问的高性能。想了解细节的同学可以去参考Linux kernel document: vfio.txt

PCI 直通架构

因为直通方式的性能损耗{BANNED}最佳小,各大公用云厂商广泛采用直通模式,而且直通方式相当于虚拟机独享GPU,因此硬件驱动无需修改。另外因为直通模式没有对GPU功能性做阉割,因此大多数功能可以在直通模式下无修改支持。但GPU 直通的缺点是一张GPU卡不能同时直通给多个虚拟机使用,相当于虚拟机独占了GPU卡。如果多个虚拟机需要同时使用GPU,需要在服务器中安装多块GPU卡,分别直通给不同的虚拟机使用。而且直通GPU的虚拟机不支持在线迁移。

为了应对GPU直通不能共享GPU的限制,第三种方式直通共享的虚拟化方式出现了。这也是目前GPU厂商主推的技术趋势,也是我们这篇文章结束的重点。在介绍直通共享方案前,首先介绍一下直通共享的原理。

GPU虚拟化技术的原理

如下图所示,是GPU的典型使用架构,从应用层到底层硬件,每一层都可以通过相关的技术实现GPU虚拟化。

按照CUDA 计算 stackOpenGL 渲染 Stack两个场景划分,又可以分为不同的虚拟化技术。如下图所示。

CUDA 计算 stack

OpenGL 渲染 Stack

一个典型的 GPU 设备的工作流程是:

1.   应用层调用 GPU 支持的某个 API,如 OpenGL CUDA

2.   OpenGL CUDA 库,通过 UMD (User Mode Driver),提交 workload KMD (Kernel Mode Driver)

3.   KMD CSR MMIO,把它提交给 GPU 硬件

4.   GPU 硬件开始工作... 完成后,DMA 到内存,发出中断给 CPU

5.   CPU 找到中断处理程序 —— KMD 此前向 OS Kernel 注册过的 —— 调用它

6.   中断处理程序找到是哪个 workload 被执行完毕了,...{BANNED}最佳终驱动唤醒相关的应用

可以看出,从 API 库开始,直到 GPU 硬件,Stack 中的每一个阶段,都有被截获、转发的可能性。下面主要以CUDA stack为例介绍。

API转发

在介绍API转发方案前首先看一下CUDA的整体架构。如下图所示:

CUDA 开发者使用的,通常是 CUDA Runtime API,它是 high-level 的;而 CUDA Driver API 则是 low-level 的,它对程序和 GPU 硬件有更精细的控制。Runtime API 是对 Driver API 的封装。

CUDA Driver 即是 UMD(User Mode Driver),它直接和 KMD(Kernel Mode Driver) 打交道。两者都属于 NVIDIA Driver package,它们之间的 ABI,是 NVIDIA Driver package 内部的,不对外公开。英伟达软件生态封闭包括:

l  无论是 nvidia.ko,还是 libcuda.so,还是 libcudart,都是被剥离了符号表的

l  大多数函数名是加密替换了的

nvidia.ko 为例,为了兼容不同版本的 Linux 内核 API,它提供了相当丰富的兼容层,于是也就开源了部分代码:

其中这个 26M 大小的、被剥离了符号表的 nv-kernel.o_binary,就是 GPU 驱动的核心代码,所有的 GPU 硬件细节都藏在其中。

CUDA架构上看,API转发分为用户态截取和内核态截取。用户层API截取是一种在用户态实现的虚拟化技术。通过创建一个函数库(如libwrapper),它能够拦截用户的API调用,解析后转发到实际的GPU驱动,从而实现资源的隔离和调度。这种方法的优势在于对用户代码零侵入,且部署灵活,无论是在裸机还是容器化环境中都易于实现。内核层拦截通过在操作系统内核空间实现模块,模拟GPU设备文件,从而拦截对GPU驱动的访问。这种方法可以有效防止用户篡改,提高安全性,但由于NVIDIA驱动的闭源性质,技术实现难度较高。

从虚拟机和host的调用关系上看,API转发分为被调方和调用方,两方对外提供同样的接口(API),被调方API实现是真实的渲染、计算处理逻辑,而调用方API实现仅仅是转发,转发给被调方。其核心架构示意如下图:

l  GPU 用户态API层的转发,业界有针对OpenGLAWS Elastic GPUOrionX,有针对CUDA的腾讯vCUDA,瓦伦西亚理工大学rCUDA

l  GPU内核驱动层的转发,有针对CUDA的阿里云cGPU和腾讯云qGPU

如下图是阿里云cGPU架构,相比其他方案:通过一个内核驱动,为容器提供了虚拟的GPU设备,从而实现了显存和算力的隔离;通过用户态轻量的运行库,来对容器内的虚拟GPU设备进行配置。阿里云异构计算cGPU在做到算力调度与显存隔离的同时,也做到了无需替换CUDA静态库或动态库;无需重新编译CUDA应用;CUDAcuDNN等版本随时升级无需适配等特性。

如下图是腾讯云的qGPUqGPU == QoS GPU)架构:

 

API转发的灵活性以及支持GPU虚拟化的数量不受限制,往往应用于serverless 容器场景

API转发方案的优点是实现了1N,并且N是可以自行设定,灵活性高。同时不依赖GPU硬件厂商。但缺点复杂度极高。同一功能有多套 API(渲染的 DirectX OpenGL),同一套 API 还有不同版本( DirectX 9 DirectX 11),兼容性非常复杂。并且功能不完整,如不支持媒体编解码,并且,编解码甚至还不存在业界公用的 API

 

硬件虚拟化

从硬件虚拟化角度又可分为Hardware Partition即空分和Time Sharing即时分。

Hardware Partition

PCIe SR-IOV

前文我们提到了GPU直通,这种通过PCIe直通GPU的方式只能支持11,不支持GPU资源分隔。于是为了解决这个问题,PCIe SR-IOV(Single Root Input/Output Virtualization)出现。AMD S7150 开始、英伟达从 Turing 架构开始,数据中心 GPU 都支持了 SR-IOV。但是它不是 NIC 那样的 SR-IOV,它需要 Host 上存在一个 vGPU device-model,来模拟从 VM 来的 VF 访问。

基于PCIe SR-IOVGPU虚拟化方案,本质是把一个物理GPU显卡设备(PF)拆分成多份虚拟(VF)的显卡设备,而且VF 依然是符合 PCIe 规范的设备。核心架构如下图:

SRIOV的本质是把一个PCI卡资源(PF)拆分成多个小份(VF),这些VF依然是符合PCI规范的endpoint设备。由于VF都带有自己的Bus/Slot/Function号,IOMMU/VT-d在收到这些VFDMA请求的过程中可以顺利查找IOMMU2nd Translation Table从而实现GPAHPA的地址转换。这一点与GVT-gNvidiaGRID vGPU(分片虚拟化)有本质上的区别。GVT-gNvidia GRID vGPU并不依赖IOMMU。其分片虚拟化的方案是在宿主机端实现地址转换和安全检查。应该说安全性上SRIOV方法要优于GVT-gGRID vGPU,因为SRIOV多了一层IOMMU的地址访问保护。SRIOV代价就是性能上大概有5%左右的损失(当然mdev分片虚拟化的MMIO trap的代价更大)。由于SRIOV的优越性和其安全性,不排除后续其他GPU厂商也会推出GPU SRIOV的方案。

这里要说一点,AMD GPU SRIOV从硬件的角度看就是一个对GPU资源的分时复用的过程(严格意义上说AMD的实现其实是一种Time Sharing),并不是真的资源切割,因此其运行方式也是与GPU分片虚拟化类似

PCIe SR-IOV的有点就是真正实现了真正实现了1N,一个PCIe设备提供给多个VM使用;但缺点是灵活性较差,无法进行更细粒度的分割与调度;并且不支持热迁移。

NVIDIA MIG

上文介绍的基于 SR-IOV 硬件虚拟化技术的 GPUVF 的数量比较固定,且每个 VF 获得的资源是均分的、定额的。将这些 VF 透传给虚拟机后,由于各个虚机的 workload 不同,就可能出现某些 VF 的资源不够用,而另一些 VF 的资源用不完的情况。

作为业界一哥的 Nvidia,自 2020 年的 Ampere 微架构(比如 A100)开始支持一种叫做 MIG (Multi Instance GPU) 的技术,GPU可以被安全分割为{BANNED}最佳多七种独立的GPU实例,服务于CUDA应用,从而使多个用户能各自拥有独立的GPU资源,以达到{BANNED}最佳佳利用率。

对于有多租户需求的云服务提供商(CSP),MIG技术确保了单一客户端的行为不会干扰其他客户端的运行或调度,同时增强了对各个客户的安全隔离性。

MIG模式下,每个实例所对应的处理器具备独立且隔绝的内存系统访问路径——片上交叉开关端口、L2缓存分段、内存控制器以及DRAM地址总线均会专一地分配给单个实例。这使得即便有其他任务在对其自身缓存进行大量读写操作或已使DRAM接口达到饱和的情况下,单个工作负载仍能获得稳定、可预期的执行速度和延迟时间,同时保证相同水平的L2缓存分配与DRAM带宽资。

MIG能够对GPU中的计算资源(包括流式多处理器或SM,以及诸如拷贝引擎或解码器之类的GPU引擎)进行划分,从而为不同的客户(例如虚拟机、容器或进程)提供预设的服务质量(QoS)保障及故障隔离机制。

运用MIG技术后,用户可以像管理实体GPU一样查看和安排在新建的虚拟GPU实例上的任务。MIG兼容Linux操作系统,支持基于Docker Engine的容器部署,同时亦对接Kubernetes及建立于Red Hat虚拟化和VMware vSphere等支持的虚拟机管理程序之上的虚拟机。

Multi-Instance,那一个 instance 具体是什么呢?

一颗 Discrete GPU 的硬件资源主要包括两类:计算单元和内部存储(video memory)。如果对它们分片,那么就形成了 compute slice 和 memory slice(每个 memory slice 有独立的 memory controller 和 cache,不损失访问带宽)。然后我可以根据 workload 的需要,将这些 slices 进行一定的组合,就形成了多个 instance(vGPU):
比如上图的 "4g.20gb",表示的是 4 compute slice20 GB 显存(4 memory slice:


划分方式多种多样,在不牺牲隔离性的同时提高了应用的灵活性:

不过 slice 的划分不是随心所欲的,不是你想切多细就能切多细(想起了涡虫),还是受到底层硬件设计的限制:

MIG 技术可视作是基于传统 SR-IOV 的演进和创新,一个 instance 大致可对应一个 PCIe VF。以 MIG 为代表的硬件资源划分主要在空间上(spatial),同一物理 GPU上的各个 vGPU 获得的资源是专属的(dedicate),可以获得良好的并行性。

GPU分片虚拟化

技术上讲GPU分片虚拟化,就是指基于VFIO mediated 透传框架的GPU虚拟化方案。该方案由NVIDIA提出,并联合Intel一起提交到了Linux 内核4.10代码库,该方案的kernel部分代码简称mdev模块。随后RedHat EnterpriseCentOS{BANNED}最佳新的发行版花了不少力气又back porting到了3.10.x内核。所以如果目前采用{BANNED}最佳新的RedHat发行版(企业版或者CentOS 7.x)等都已经自带mdev模块。而如果采用Ubuntu 17.x以后版本的话,不但自带mdev功能,连Intel GPU驱动(i915)也已经更新到支持vGPU。无需任何代码编译就可以直接体验vGPU虚拟机功能。

那么什么叫mediated透传呢? 它与透传的区别是什么呢? 一句话解释:把会影响性能的访问直接透传给虚拟机,把性能无关和功能性的MMIO访问做拦截并在mdev模块内做模拟(是不是和网卡虚拟化vDPA的思想一样)

如果熟悉网卡虚拟化vDPA技术的同学一定都对mdev不陌生,没错,mdev{BANNED}最佳早就是为GPU分片虚拟化而生的。另外要说明一点关于vGPU这个词的含义,它有两层含义,通用的含义是只通过虚拟化技术虚拟出的虚拟GPU都可以称作vGPU,但是有时也特指NVIDIA GRID vGPU技术。

GPU分片虚拟化的方案被NVIDIAIntel两个GPU厂家所采用。NVIDIA GRID vGPU系列与IntelGVT-gXenGTKVMGT)系列。

当然,只有内核的支持还不够,需要加上QEMU v2.0 以后版本,以及Intel或者NVIDIA自带的GPU mdev驱动(也就是对GPU MMIO访问的模拟),那么GPU分片虚拟化的整个路径就全了。而GPU厂家的mdev驱动是否开源取决于自己。按照一贯的作风,Intel开源了其绝大部分代码,包括{BANNED}最佳新的基于mdevGPU热迁移技术,而NVIDIA也保持其一贯作风:不公开。

GPU分片虚拟化看起来整个框架就如下图一样(以KVMGT作为例子):

可以从上图看到,vGPU的模拟是通过kvmGTIntel)或者NVIDIA-vgpu-vfioNVIDIA)来完成的。该模块只模拟对MMIO的访问,也就是功能性,不影响性能的GPU寄存器。而对GPU apertureGPU graphic memory则通过VFIO的透传方式直接映射到VM内部。

值得注意的是一般Pass through的方式都依赖IO MMU来完成GPAHPA的地址转换,而GPU的分片虚拟化完全不依赖IO MMU,也就是说其vGPUcmd提交(内含GPA地址)并不能直接运行于GPU硬件之上,至少需要有一个GPAHPA的翻译过程。该过程可以通过host端的cmd扫描来修复(KVM GT),NVIDIA GRID vGPU每一个上下文有其内部页表,会通过修改页表来实现。

需要注意的是,NVIDIA GRID vGPU是收费的,所以企业用户要去官网购买license才可以使用。

下面是MIGvGPU的一个能力对比:

 

Time Sharing

NVIDIA MPS

MPS NVIDIA Multi-Process Service )是NVIDIA公司为了进行GPU共享而推出的一套方案,由多个CUDA程序共享同一个GPU context,省去了 Context Switch 的开销,也在 Context 内部实现了算力隔离,从而达到多个CUDA程序共享GPU的目的。同时,在Volta GPU上,用户也可以通过CUDA_MPS_ACTIVE_THREAD_PERCENTAGE变量设定每个CUDA程序占用的GPU算力的比例。

该方案和PCIe SR-IOV方案相比,配置很灵活,并且和docker适配良好。MPS基于C/S架构,配置成MPS模式的GPU上运行的所有进程,会动态的将其启动的内核发送给MPS serverMPS Server借助CUDA stream,实现多个内核同时启动执行。

但该方案的一个问题在于,各个服务进程依赖MPS,一旦MPS进程出现问题,所有在该GPU上的进程直接受影响,需要使用Nvidia-smi重置GPU 的方式才能恢复。

Time-Slicing GPU 

NVIDIA GPU 支持基于 Engine Context Switch。不管是哪一代的 GPU,其 Engine 都是支持多任务调度的。一个 OS 中同时运行多个 CUDA 任务,这些任务就是在以 Time Sharing 的方式共享 GPU。这种虚拟化方式就和 CPU 虚拟化就比较像了。vGPU 串行地调度执行,一个 vGPU 需等待其他 vGPU 让出物理 GPU,但当它获得物理 GPU 时,其对 GPU engine 的使用是 exclusive 的。

这种方式非常适合容器runC的场景,为此NVIDIA还提供给了对k8s的支持:

 

总结

    以上就是GPU目前的场景虚拟化技术,其实按照“空分”和“时分”只是一个大概分类,很多技术,如分片虚拟化其实是两者的结合。下图是NVIDIA GPU虚拟化的发展实际线:

以及NVIDIA GPU虚拟化技术对比

阅读(42) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~