用户层虚拟化
本地 API 拦截和 API formwarding
-
在用户态实现一个函数库,假设叫 libwrapper, 它要实现底层库的所有 API;
-
让 APP 调用这个 libwrapper。如何做?
-
libwrapper 拦截用户的函数调用,对参数进行解析,然后使用参数去调用实际的底层库相同名称的函数。
-
调用完成后,libwrapper 把结果返回给 APP;
实现关键:
-
APP 和底层可的静态链接变成动态链接;
-
libwrapper 使用类似 dlopen 打开底层库;
远程 API forwarding
-
libwrapper 通过网络,去调用不同机器上的底层库
-
libwrapper 变为两部分
- client:拦截,转发
- server:接收,调用
可以实现 GPU 池化:即多个 GPU 可以组成调用池,由多个 client 来调用,可以做到让不具备 GPU 的机器能实现 GPU 的功能。实现关键:
-
调用函数类似 RPC,参数需要进行序列化和反序列化;
-
相对本机来说,远程数据传输的性能对函数调用的延迟影响很大。一般可以通过 RDMA 进行网络加速。
半虚拟化 API forwarding
-
APP 和 libwrapper 运行在虚机中;
-
libwrapper 通过半虚拟化(virtio)进行通讯,调用宿主机的底层库;
-
VM 的内核要实现 virtio frontend
-
Host 的 Hypervisor 要实现 virtio backend
-
宿主机完成底层库的调用
内核层虚拟化
内核层 GPU 驱动拦截
实现原理:内核模块通过设备文件拦截
-
通常底层库通过设备文件访问 GPU 驱动的功能,假设为/dev/realgpu
-
实现一个内核模块,输出模拟的设备文件给用户空间,/dev/fakegpu
-
把模拟的设备文件 bind mount 到容器里,伪装成真的设备文件,/dev/realgpu
-
APP 和底层库都在容器里运行,底层库访问伪装的设备文件/dev/realgpu,此时所有访问被内核模块拦截;
实现关键
-
需要了解底层库调用 GPU 驱动的系统调用的含义;
-
内核拦截模块只需要拦截必要的系统调用;
-
适合容器应用;
内核层 GPU 驱动半虚拟化
实现原理:GPU 驱动半虚拟化
-
APP 和底层库都在 VM 里;
-
VM 的 GPU 驱动实现半虚拟化接口,通过类似 hpyercall 的方式,调用 Host 实际的 GPU 驱动;
-
hpyercall 切换 guest 到 Hypervisor,Hypervisor 通过内核中的驱动代理来访问实际的 GPU 驱动;
实现关键
-
适合 VM 应用;
-
结合硬件虚拟化支持类似 vGPU 功能;
硬件虚拟化
全虚拟化/透传 GPU
全虚拟化,Full Virtualization,又称透传 GPU。
-
虚机的 GPU 驱动,不需要做任何修改,基本上访 问的是 GPU 真实的硬件资源;
-
整个 GPU 透传给虚机,性能损耗最小;
-
因为无法实现 GPU 资源共享,一般认为不属于 GPU 虚拟化。
NVIDIA vGPU
-
虚拟机内核需要安装特定的虚拟化驱动:GRID 驱动。
-
显存按照固定切分,直接分配给 VM;
-
算力调度采用时分方案,按时间片分配给 VM;
NVIDIA MIG
NVIDIA 硬件切分方案:MIG
-
MIG:Multi-Instance GPU,NVIDIA 自己退出的硬件资源切分方案。可以直接把 GPU 切分多分,每一份资源硬件隔离;
-
每一 MIG 的切分,可以当作一个 GPU 来使用,同时可以结合容器来部署;
-
目前仅在 A30、A100、H100 显卡上支持;
NVIDIA MIG vGPU
MIG 和 vGPU 的结合:NVIDIA MIG vGPU
-
显存,算力都按照 MIG 硬件切分,直接分配给 VM;
-
算力损耗相对 vGPU 小;
AMD MxGPU
基于 SRIOV 方式切分 GPU 为 VF
Intel GVT-g
类似 Nvidia vGPU