用户层虚拟化
本地 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