云原生GPU虚拟化技术sGPU

原创
2023/12/28 10:33
阅读数 35

01

背景

近年来随着深度学习的快速发展,AI在很多领域的应用越来越广泛,算力作为推动AI快速发展的三驾马车之一,起到了至关重要的作用。而GPU作为AI算力的主要资源,其使用规模越来越大,计算效率和使用成本也越来越受到行业的关注。
在OPPO内部,GPU的使用场景非常多,包括大模型,语音语义,图片识别,推搜广等,用卡规模也很大,其利用率方面仍然面临一些问题和挑战,主要几种在以下几个方面:
  1. 业务容器独占GPU卡,但无法充分使用单卡算力

2. 某些业务场景GPU利用率偏低

3. 在线推理服务存在明显的波峰波谷现象

为了满足业务对GPU算力快速增长的需求,OPPO云采取了一系列技术手段不断提高GPU利用率,其中GPU虚拟化技术就是其中之一。

02

方案

2.1 技术演进

多年来,对于怎样通过复用GPU来提升使用率的探索一直没有停止过,从最早的无隔离直接复用,到应用层隔离vCUDA,再到NVIDIA的vGPU,MPS,MIG。近年来,行业内又出现了一种通过GPU的虚拟化技术来实现GPU复用的新方案。该方案的核心组件在内核态实现,不仅可以做到很高的显存和算力隔离,而且对业务无感,不需要劫持用户态大量API接口。

多种方案各有优缺点,以下是多维度详细对比:

在对现有技术做了充分调研后,OPPO于两年前自研GPU的虚拟化技术方案sGPU(sharedGPU)。简单来说,sGPU是一种面向云原生的内核态GPU虚拟化技术,包含了从k8s调度,到容器运行时,内核模块,账单,监控等一整套技术方案。用于多个业务容器共享同一张GPU卡,支持显存隔离限制及基于权重的算力调度。可以有效提高GPU利用率,显著降低资源使用成本。

2.2 sGPU功能特点
sGPU 方案通过对 NVIDIA GPU 卡上任务更有效的调度,达到给多个容器共享使用的目的,支持的功能如下:
灵活性:用户可以自由配置 GPU 的显存大小和算力占比
云原生:支持标准的 Kubernetes,兼容 NVIDIA Docker 方案
兼容性:镜像不修改/CUDA 库不替换/业务不重编,易部署,业务无感知
高性能:在底层对 GPU 设备进行操作,高效收敛,吞吐接近0损耗
强隔离:支持显存和算力的严格隔离,业务共享不受影响
除了上述基本的功能特点,sGPU还提供了诸多产品可调试,可维护及可优化相关的配置,支持更多高级特性。
1. 可视化(丰富的stats,logs,nvidia-smi可以显示进程信息),可调试(动态日志级别)
2. 更强的容器隔离性(禁用了容器内的显卡修改操作)
3. 可配置调度策略及时间片, 小时间片适用于低延迟推理,大时间片适用于高性能训练
4. 支持空闲容器时间片抢占,识别容器内进程有无计算任务,避免浪费时间片
5. 支持整卡,方便sGPU资源池弹性扩缩容,避免浪费
6. 结合MPS,解决GPU调度时间片轮转导致的高延迟问题
7. 支持单容器多虚拟卡
2.3 sGPU架构
sGPU作为一套完整的技术方案,其整体架构如下所示:

sGPU的核心组件包括:
  • sGPU内核模块:通过创建GPU虚拟卡,截获转发GPU设备请求,实现对容器的显存及算力的隔离限制。
  • sGPU容器运行时组件:基于原生开源组件nvidia-docker定制开发,主要负责容器内显卡设备挂载,容器GPU资源配置,及容器安全校验。
  • sGPU Device Plugin(设备管理插件),部署在每台 sGPU服务器上,负责将GPU卡数和显存资源上报至 kubernetes ,同时为调度在本节点上的 sGPU容器实际分配显存资源;
  • sGPU Scheduler Extender(调度器扩展),向集群 kube-scheduler 服务提供 Filter 和 Bind 扩展接口,为 sGPU 容器选择合适的节点进行绑定;
业务创建一个sGPU容器,从机器学习平台操作到创建出符合业务要求的容器,需要经过包括k8s,kubelet,docker,sgpu_km等多个组件的协同工作,其整体流程如下图所示:

03

技术原理

3.1 显存隔离

NVIDIA GPU有自己的独立显存,并提供了多种显存类型, CUDA计算任务在执行前,其kernel代码和依赖的数据都需要拷贝到GPU显存。显存模型中,全局显存(Global Memory)是最大的区域,该区域也是显卡通常标称的大小,例如T4显卡的16GB显存。全局显存的使用有两种方式,一种是静态分配,如CUDA Runtime初始化时自动分配的全局变量等显存。另一类为CUDA应用程序通过API动态分配的显存,如cudaMemAlloc()。同时为了提高全局显存的访问效率,显卡也实现了多级缓存的内存访问模型,如下图所示:

说明:
Global Memory: 所有显存共享,可读可写,访问速度相比其他类型慢。
Texture Memory:为texture数据访问优化设计,所有线程共享,只读,访问速度较慢。
Constant Memory:常量显存区域,只读,访问慢,但有缓存,面向所有线程。
Shared memory:SM内部显存,访问速度快,面向同一block的线程。
Registers:速度快,线程级别私有。
sGPU的显存隔离针对CUDA应用程序动态分配的全局显存(Global Memory),CUDA显存管理的API在CUDA驱动层收敛为nvidiactl设备的ioctl操作,sGPU通过创建虚拟GPU卡设备从而截获住ioctl操作,进而实现显存的隔离与限制,其软件栈分层架构如下图所示:

sGPU的实现要点:
  1. 容器级别的隔离限制,通过创建GPU虚拟卡设备,挂载到容器内,实现内核模块对显存分配,释放,查询接口的劫持和转发。
  2. 显存分配:内核模块截获到显存分配请求时,如果请求分配的size超过容器的限制,则返回OOM,否则记录进程分配显存的相关信息,转发请求到NV内核驱动。
  3. 显存释放:内核模块截获到显存释放请求时,查询到分配记录,进行释放,然后转发到NV内核驱动。进程异常退出时,自动释放进程分配的所有显存。
  4. 显存查询:截获请求后,返回容器隔离限制后的显存总大小及剩余大小。
3.2 算力隔离
NVIDIA的GPU默认的算力调度是time-sliced即轮转调度,默认时间片为2ms。调度的最小单元为CUDA上下文Context,CUDA在启动一个进程时都会创建一个context。每个context有自己的地址空间,并且不能访问其他context的地址空间。

默认的NV调度器是面向进程级别的,而sGPU则在默认的NV调度器之上实现了面向容器级别的算力调度,根据容器的算力权重进行时间片轮转调度。其实现原理如下图所示:

sGPU实现要点:
  1. 容器级别的隔离限制,内核模块截获CUDA Context创建的请求,记录跟踪容器内所有进程的CUDA Context。
  2. 每张显卡创建一个内核调度线程,负责根据容器的算力权重,对卡上所有容器的Context进行启停调度。
  3. 支持四种调度策略:自由调度,权重调度,平均抢占调度,权重抢占调度。
  4. 支持可配置时间片,减少容器级别Context切换开销。

04

实现

作为一种虚拟化方案,其隔离性及性能是最重要的两个方面。在sGPU产品上线前,对其隔离性,稳定性及其性能进行了大量测试,以下分别进行介绍。
以下所有测试均采用TF_CNN_Benchmarks的ResNet50模型分别测试训练和推理在不同精度和batch_size下的性能比较。
4.1 隔离效果
隔离性方面,理论上来说MIG的隔离效果最好,因为其属于硬件强隔离,空分方案。sGPU和MPS都属于软件时分方案,理论上隔离效果要差一些。我们看一下实际测试情况如何。
测试机器为一台A100显卡服务器,GPU显存80G,96核CPU,1TB内存。
创建两个容器,算力配比2:1,对比MPS,MIG及sGPU的隔离性表现,结果如下图所示(横坐标为计算精度和batch size, 纵坐标为运行时两个容器的实际算力占比)。

从图中可以看出,在A100训练场景下,NVIDIA硬件方案MIG的隔离性和sGPU基本相当,算力隔离比例基本稳定在2:1,随着不同精度和batch size都存在些许波动。MPS的隔离效果稍差一些。
4.2 性能测试

使用sGPU之前,业务方首要关心的问题就是性能问题。下面我们针对单容器和多容器场景分别进行测试。

单容器性能比较
理论上来说,GPU满负载的情况下,单sGPU容器的性能可以做到基本无损耗,看看实际测试结果如何。
创建一个sGPU容器,分配单卡所有显存及算力,对比标准的GPU容器,看看虚拟化技术本身是否会存在性能损耗。
测试机器为一台V100显卡服务器,GPU显存32G,80核CPU,500GB内存。

从上图可以看出,相比单容器原生容器,sGPU容器几乎看不出来什么差异。说明单容器的情况下,sGPU技术本身对性能是几乎没有损耗的。

多容器性能比较
下面重点看下多容器共享GPU的情况下性能表现如何。
  • 推理
首先测试推理服务的性能,两个容器,各配置1/2显存和算力。测试机器为一台T4显卡服务器,GPU显存16GB,64核CPU,380G内存。

从上图测试结果可以看出,在多种精度和batchsize的组合下,sGPU相比直通和MPS会差一些,大概都在1%~10%以内。
  • 训练
由于标准容器不支持显存隔离,所以无法测试多容器共享GPU的场景。上文有提到NVIDIA提供了两种多容器共享的隔离技术,MPS和MIG。下面我们就以这两种技术作为参照技术,进行性能测试对比。
测试机器为一台A100显卡服务器,GPU显存80G,96核CPU,1TB内存。

从上图测试结果可以看出,MPS在性能方面确实有明显优势,sGPU和MIG的性能相差不大,尽管MIG是硬件隔离技术,但由于其实例切分的限制,两个容器共享GPU时,只能切分为3/7, 3/7, 会存在1/7算力的浪费。

4.3 遇到的问题

1. 延迟问题
NV的GPU调度是基于ctx的,时间片轮转,一个时间片只能执行一个ctx,即两个进程并不能在同一个时间片并发执行。所以多容器共享GPU时,必然会对服务的延迟产生影响,尤其是对于延迟特别敏感的业务,如单请求的延迟在ms级或者us级的服务。
请求处理延迟 = 轮转次数 * 容器个数 * 时间片
其中轮转次数 = 整卡请求延迟 / 时间片 (向上取整)
为了解决该问题,OPPO采用了sGPU结合MPS的方案,利用MPS的ctx合并技术,将多容器的ctx合并为一个ctx,从而获得更高的并发度,避免ctx的轮转调度,从而减低延迟。但结合MPS技术需要解决故障隔离问题。
2. 时间片空转问题
sGPU最初的算力隔离版本测试中发现一个时间片空转问题,在调度容器内程序的ctx时,如果该程序没有计算任务,比如在sleep,则分配给该容器的时间片就浪费掉了。

而原生GPU调度的情况下,则不存在这个问题,GPU调度器可以判断一个ctx是否有计算任务。

该问题比较棘手,需要对GPU的调度原理及控制进行深入研究与分析,才能解决。

3. 隔离安全性问题(smi)
多容器共享GPU时,会引入一个安全隔离性问题,容器内可以通过smi命令或者nvml接口操作显卡,比如设置显卡为Exclusive模式,造成共享该显卡的其他容器无法正常使用GPU。为了加强容器隔离安全性,我们在sGPU内核模块对越权操作进行了拦截。
4. 容器内缺少进程信息(smi)

GPU容器内,通过nvidia-smi命令无法查看到进程信息,影响了用户体验及问题排查效率。我们在内核模块中通过namespace级别的进程信息转换,实现了容器和宿主机一致的用户体验。

05

总结

OPPO的sGPU技术方案,通过内核态GPU虚拟化技术,有效解决了容器共享GPU卡的显存及算力隔离的问题,为公司的推理和训练业务共享GPU提供了有效保障。上线近两年来,运行稳定,业务完全无感知,已推广接入机器学习相关业务应用近百个,为业务节省了35~70%的GPU账单成本,GPU复用率大幅提升,推理场景下目前复用率为2.19,训练场景下目前复用率为1.8,为OPPO云上GPU资源的利用率的不断提升提供了有效的技术手段。为了适用于更多的业务场景,未来我们还计划结合用户态方案,实现显存超卖等高级功能。
作者介绍

Jin Xu  OPPO高级后端工程师

负责OPPO云GPU异构计算及虚拟化,大模型推理引擎优化,镜像系统架构及P2P分发等技术。

END
About AndesBrain

安第斯智能云

安第斯智能云(AndesBrain)是服务个人、家庭与开发者的泛终端智能云,致力于“让终端更智能”。安第斯智能云提供端云协同的数据存储与智能计算服务,是万物互融的“数智大脑”。

安第斯大模型(AndesGPT)是OPPO自主训练的、个性专属大模型与智能体。作为驱动OPPO公司AI战略的核心引擎,安第斯大模型全面赋能OPPO智慧终端,持续构建知识、记忆、工具、创作能力,并通过与终端结合的AI智能体和多模态对话范式,给用户带来全新的个性专属智能体验。

本文分享自微信公众号 - 安第斯智能云(OPPO_tech)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部