第三章 浅谈 GPU 虚拟化技术(三)GPU SRIOV 及 vGPU 调度
GPU SRIOV 原理
谈起 GPU SRIOV 那么这个世界上就只有两款产品: S7150 和 MI25. 都出自 AMD, 当然 AMD 的产品规划应该是早已安排到几年以后了, 未来将看到更多的 GPU SRIOV 产品的升级换代. S7150 针对的是图形渲染的客户群体, 而 MI25 则针对机器学习, AI 的用户群体. 本文以围绕 S7150 为主. 因为 S7150 的 SRIOV 实例在各大公有云市场上都有售卖, 而 MI25 目前看来尚未普及(受限于 AMD ROCm 生态环境的完备性).
两个术语: SRIOV 的 PF,VF
(专业人士请自动忽略这部分介绍
)
PF: 宿主机上的主设备, 宿主机上的 GPU 驱动安装在 PF 上. PF 的驱动是管理者. 它就是一个完备的设备驱动, 与一般的 GPU 驱动的区别在于它管理了所有 VF 设备的生命和调度周期. 比如下图的 07:00.0 便是 PF 设备
VF: 也是一个 PCI 设备, 如下图中的 07:02.0 和 07:02.1.QEMU 在启动过程中通过 VFIO 模块把 VF 作为 PCI 直通设备交由虚拟机, 而虚拟机上的操作系统会安装相应的驱动到这个直通的 VF PCI 设备上(07:02.0).VF 设备占用了部分 GPU 资源. 比如下图中一个 PF 上面划分出了两个 VF, 那么很有可能跑在 VF 上面的虚拟机 GPU 图形渲染性能宏观上是 PF 的 1/2.
上图是一个带有 4 个 S7150 的服务器, 并且每个 S7150 SRIOV 虚拟出 2 个 vGPU.
GPU SRIOV 的本质
SRIOV 的本质是把一个 PCI 卡资源 (PF) 拆分成多个小份(VF), 这些 VF 依然是符合 PCI 规范的 endpoint 设备. 由于 VF 都带有自己的 Bus/Slot/Function 号, IOMMU/VTD 在收到这些 VF 的 DMA 请求的过程中可以顺利查找 IOMMU2nd Translation Table 从而实现 GPA 到 HPA 的地址转换. 这一点与 GVT-g 和 Nvidia 的 GRID vGPU 有本质上的区别. GVT-g 与 Nvidia GRID vGPU 并不依赖 IOMMU. 其分片虚拟化的方案是在宿主机端实现地址转换和安全检查. 应该说安全性上 SRIOV 方法要优于 GVT-g 和 GRID vGPU, 因为 SRIOV 多了一层 IOMMU 的地址访问保护. SRIOV 代价就是性能上大概有 5% 左右的损失(当然 mdev 分片虚拟化的 MMIO trap 的代价更大). 由于 SRIOV 的优越性和其安全性, 不排除后续其他 GPU 厂商也会推出 GPU SRIOV 的方案.
关于 SRIOV 更多的思考
SRIOV 也有其不利的地方比如在 Scalable 的方面没有优势. 尤其是 GPU SRIOV, 我们看到的最多可以开启到 16 个 VM. 设想如果有客户想要几百个 VM, 并都想要带有 GPU 图形处理能力(但是每个 VM 对图形渲染的要求都很低), 那么 SRIOV 的方案就不适用了. 如果有一种新的方案可以让一个 GPU 的资源在更小的维度上细分那就完美了. 事实上业界已经有这方面的考虑并付诸实践了.
GPU SRIOV 内部功能模块
(吃瓜群众可以跳过)
由于没有 GPU SRIOV HW 的 spec 与 Data Sheet, 我们仅能按照一般的常用的方式来猜测 GPU SRIOV 内部功能模块(纯属虚构, 如有雷同概不负责).
GPU 的资源管理涉及到 vGPU 基本上三块内容是一定会有的: Display, 安全检查, 资源调度.
Display 管理
GPU PF 需要管理分配给某个 VF 的 FrameBuffer 大小, 以及管理 Display 相关的虚拟化. Display 的虚拟化一般分为 Local Display 和 Remote Display. 比如 XenClient 就是用的 Display Local Virtualization, 属于本地虚拟化过程. 此过程相当于把显示器硬件单元完全交由当前虚拟机控制. 在云计算行业, Display 更多的是采用 Remote Display 的方式. 我们后续会讲到行业中 Remote Display 的问题所在.
VF 安全检查
GPU PF 或者 GPU SRIOV 模块需要承担一部分的 VF 的地址审核 (Address Audit) 和安全检查, GPU SRIOV 的硬件逻辑会保证暴露出的 VF Register List 并确保不包含特权 Register 信息, 比如针对 GPU 微处理器和 FW 的 Registers 操作, 针对电源管理部分的 Registers 也不会导出到 VF 中. 而 VM 对所有 VF 的 MMIO 读写最终会映射到 PF 的 MMIO 地址空间上, 并在 PF 的类似微处理器等地方实现 VF 设备的部分 MMIO 模拟.
另外一部分的安全检查则是 PF 需要确保不同 VF 直接对 GPU FrameBuffer 的访问隔离. 这部分很有可能需要 PF 针对不同的 VF 建立 GPU 的 Pagetable, 或者 Screen 所有的 VF 提交的 GPU BatchBuffer.
VF 调度
AMD GPU SRIOV 从硬件的角度看就是一个对 GPU 资源的分时复用的过程. 因此其运行方式也是与 GPU 分片虚拟化类似. SRIOV 的调度信息后续重点介绍.
GPU SRIOV 的调度系统
分时复用
VF 的调度是 GPU 虚拟化中的重点, 涉及到如何服务 VM, 和如何确保 GPU 资源的公平分片.
GPU SRIOV 也是一个分时复用的策略. GPU 分时复用与 CPU 在进程间的分时复用是一样的概念. 一个简单的调度就是把一个 GPU 的时间按照特定时间段分片, 每个 VM 拿到特定的时间片. 在这些时间片段中, 这个 VM 享用 GPU 的硬件的全部资源. 目前所有的 GPU 虚拟化方案都是采用了分时复用的方法. 但不同的 GPU 虚拟化方案在时间片的切片中会采用不同的方法. 有些方案会在一个 GPU Context 的当前 BatchBuffer/CMDBuffer 执行结束之后启动调度, 并把 GPU 交由下一个时间片的所有者. 而有些方案则会严格要求在特定时间片结束的时候切换, 强行打断当前 GPU 的执行, 并交予下一个时间片的所有者. 这种方式确保 GPU 资源被平均分摊到不同 VM.AMD 的 GPU SRIOV 采用的后一种方式. 后续我们会看到如何在一个客户机 VM 内部去窥探这些调度细节
调度开销
然而 GPU 的调度不同于 CPU 的地方是 GPU 上下文的切换会天然的慢很多. 一个 CPU Core 的进程切换在硬件的配合下或许在几个 ns 之内就完成了. 而 GPU 则高达几百 ns(比如 0.2ms-0.5ms). 这带来的问题就是 GPU 调度不能类似 CPU 一样可以频繁的操作. 举一个例子: GPU 按照 1ms 的时间片做调度, 那么其中每次调度 0.5ms 的时间花在了上下文的切换上, 只有 1ms 的时间真正用于服务. GPU 资源被极大浪费. 客户理论上也只能拿到 66% 的 GPU 资源.
S7150 的调度细节
接下来我们来看一下作为首款 GPU SRIOV 方案的 S7150 是如何调度的. 由于 S7150 是中断驱动的结构, 所以通过查看虚拟机内部 GPU 中断的分布情况就可大致判断出 GPU SRIOV 对这个虚拟机的调度策略.
对于 Windows 的客户机, 我们可以在内部安装 Windows Performance kit, 并检测 "GPU activity" 的活动.
对于 Linux 的客户机, 则更简单, 直接查看 GPU 驱动的 trace event. 当然我们要感谢 AMD 在提供给 Linux 内核的 SRIOV VF 驱动上没有去掉 trace event. 这让我们有机会可以在 VM 内部查看到 SRIOV 的调度细节.(不知道这算不算一种偷窥?)
我们在阿里云上随便开启一台 GA1 的 1/2 实例.
并选择 Ubuntu(预装 AMD 驱动)作为系统镜像;
在 Console 下查看所有的 GPU 相关的 trace 如下表:
很不错, 我们发现有两个 GPU 驱动分发 workload 的 event:amd_sched_job 与 amd_sched_process_job.
在 VNC 中开启一个 GPU Workload 以后(比如 Glxgears 或者 Glmark, 当然我们需要先开启 x11vnc), 我们通过下面 Command 来采集 GPU 数据.
查看我们抓取这两个 event 的事件并记录下来几个有趣的瞬间:
所有的 log 在一段时间内是连续的, 然后断开一段时间, 然后又连续的 workload 提交.
截图上的小红框是我们需要关注的间隔时间. 摘取如下表:
很明显在上述时间窗口期内当前 VM 的 GPU 被暂停了, 并被切换至服务其他 VM. 因此当前 VM 的 GPU workload 会积压在驱动层次.
我们把所有的 event 在图表上打点后就可以发现, 对于一个 1/2GPU 实例的 VM 来说, 它占用的 GPU 资源是基本上以 6ms 为时间片单位做切换的.
作图如下:
估算 vGPU 的调度效率
我们假设每次 vGPU 的调度需要平均用到 0.2ms, 而调度的时间片段是 6ms, 而从上图的结果来看, AMD GPU SRIOV 是采用严格时间片调度策略. 6ms 一旦时间用完, 则马上切换至下一个 VM(哪怕当前只有一个 VM, 也会被切走). 所以 1/2 实例的 S7150 的调度效率可以达到: 96.7% 如果有两个这样的 VM 同时满负荷运行, 加起来的图形渲染能力可达到 GPU 直通虚拟化的 96.7% 以上.
实测结果如下:
1/2vGPU+ 1/2vGPU = 97.4% (vs GPU 直通性能)
每一个 vGPU 可以达到直通 GPU 性能的 48.x%, 整体性能可以达到 97.4%, 与我们的预估非常接近.
更多的关于 GPU 虚拟化调度的思考
不得不说 AMD S7150 在 vGPU 调度上是非常成功的. AMD 的 GPU 硬件设计保证了可以在任何当前 GPU Batch Buffer 的执行过程中可以被安全的抢占(GPU Workload Preemption), 并切换上下文到一个新的 Workload. 有了这样卓越的硬件设计, 才使得 PF 驱动在软件层面的调度算法可以如此从容有序. 6ms 强制调度保证了多 VM 在共享 GPU 资源的情况下不会饥饿不会过度占用. 调度开销极小(2-3%). 而且这样的设计在 VM 数量不多的情况下可以进一步调整时间片的大小比如 12ms, 则 GPU 的利用率会更进一步提高. 那么为什么不能采用 100ms 调度呢? 因为 Windows 内核对 "GPU activity" 的活动有监视. 任何 GPU CMD 在 2 秒内没有响应, Windows 就会发起 Timeout Detected Recover(TDR), 重置 GPU 驱动. 设想如果你有 16 个 VM, 调度时间片为 100ms 的情况下, 平均一个 VM 轮转到 GPU 资源的最小间隔就有 1.6s. 加上其他由于 PF 驱动被 Linux 内核调度的延迟, 很有可能触发 Windows Guest 内部的 TDR.
来源: https://yq.aliyun.com/articles/584544