桔妹导读:GPU虚拟机实例创建速度慢是公有云面临的普遍问题,由于通常情况下创建虚拟机属于低频操作而未引起业界的重视,实际生产中还是存在对GPU实例创建时间有苛刻要求的业务场景。本文将介绍滴滴云在解决该问题时的思路、方法、并展示最终的优化成果。
从公有云服务商那里购买过虚拟主机的资深用户,一般会发现这么个规律:创建一台CPU虚拟主机是比较快的,但是要创建一台包含GPU卡的虚拟主机通常需要等比较长的时间,整个创建过程短则数十秒钟,长则数分钟。对于绝大多少的用户来说,虚拟主机的创建时间长一点对他们影响并不大,因为创建虚拟机属于相对低频操作。但是也会有一些特定的用户由于其业务场景交互性比较强,会对虚拟主机的创建时间有相对苛刻的要求,因为过长的创建时间会导致其业务用户体验很差。本文将从虚拟化的角度来介绍GPU虚拟主机创建时间长背后的原因,以及相关的优化方法。
通过分析Libvirt, QEMU以及Guest 内的相关日志及对应的时间戳,可以获取GPU虚拟主机在创建过程中的耗时情况,这里我们主要关心几个关键的时间点: a) Libvirt 开始创建QEMU 进程;b) Libvirt 执行 Resume启动VCPU ; c) Guest kernel 打印第一条日志. 在本文中,我们把a和 b 之间的时间间隔称为QEMU初始化时间, 把b 和c 之间的时间间隔称为 BIOS执行时间。以下数据是在滴滴云的线上环境中采集到的创建一台包含8个CPU核虚拟机实例的相关数据:
从上面的数据可以看到,对于规格相同的虚拟机实例,带1块P40卡的GPU实例相比同规格的CPU实例在QEMU初始化及BIOS执行部分的时间都明显要长, 在带4块P40卡以及更大内存规格的场景下,需要的时间会进一步拉长。通过实验我们发现在主机配置和GPU卡型号确定的前提下,GPU实例的创建时间长短主要取决于两个因素:虚拟机的内存大小和GPU卡的数量。
为什么GPU实例的创建过程要比CPU实例的创建过程耗时长?多消耗的时间到底花在哪里?要搞清楚原因需要深入的分析,比较直观的办法就是通过perf采样来生成火焰图,以此来分析虚拟机在创建过程中的热点函数。下图是在滴滴云环境里抓取到的GPU虚拟机启动过程中QEMU进程的火焰图。
通过对代码调用关系的分析,可以得知热点发生在系统分配内存和对内存页面清零的过程中,是由QEMU中的vfio_dma_map函数在执行VFIO_IOMMU_MAP_DMA ioctl 系统调用所触发,该调用会Pin住所有分配给VM当做RAM使用的内存。在Pin 内存的过程中,如果虚拟内存对应的物理页面尚未分配,会先进行物理内存分配并对内存页面内容进行清零。在Linux kernel 中,对分配给应用程序的内存进行清零主要是基于安全方面的考虑,避免Host 内存中的内容泄漏给用户空间的应用程序。这里之所以要将内存Pin 住,目的是为了保证IOMMU IO页表和 host HVA->HPA 映射的一致性,否则Guest 内设备的DMA操作可能会访问到错误的内存页面。
VFIO DMA 映射处理慢可以在一定程度上解释为什么内存的大小和GPU卡的数量会影响到GPU实例的创建时间。虚拟机实例内存规格越大,需要映射和Pin住的内存量也就越大,相关处理的耗时和内存量成正比。另外GPU卡上通常会包含一块比较大的MMIO区域,对MMIO的映射也会耗费较多的时间,卡的数量越多,耗时就会越长。相比之下,CPU实例的创建过程没有VFIO DMA 映射的相关处理流程,因此会比较快。
针对以上的热点,有什么办法可以消除或者缓解呢?已经有业内的同行们提到过这个问题并给出了对应的解决方案,其思路是对分配给VM 用作RAM使用的内存区域做一个标记,在内核中跳过对标记的内存页面进行清零,而将清零的动作留给QEMU来做,在QEMU 中可以利用多线程以及更高效的指令进行清零动作,从而加速Pin内存的过程。该方案的缺陷主要有两点: 一是存在安全性风险,其他应用程序可以利用设定的标记来窥探host 内存中的信息;二是在VM实例的VCPU个数比较少的情况下,优化效果不是很好。
我们采用了另外一种方案,通过修改Host kernel的内存管理部分, 我们实现了一种对Host 上空闲物理内存提前进行清零的机制,清零动作可以在系统空闲的时候进行,当某个内存页面被清零后,将其对应的 struct page 进行标记,这样在需要对内存进行清零的时候,可以通过检查该标记来判断是否要执行清零动作,如果清零的标记已经被设置,就可以跳过清零的步骤。该方案避免了上述方案中的两个主要问题,同时还有其它方面的好处,主要包括以下几点:a.可以提高缺页异常处理效率,尤其是透明大页的缺页异常处理效率;b. 可以加速需要Pin内存及需要通过mlock 来锁住内存的应用场景,例如使用RDMA, QAT 硬件加速等场合;c. 可以加速内核中其他需要对内存进行清零的场景。相关补丁的RFC版本,我们已经提交到了Linux kernel 社区。
另一个加速Pin内存的有效方法是采用大页,通过开启透明大页可以显著减少缺页处理的调用次数并加速Pin内存的过程。下图展示了开启透明大页以及启用空闲内存预清零机制对GPU实例创创建时间的影响。
以上的数据表明,在开启透明大页以及空闲内存预清零功能后,可以显著的的优化QEMU的初始化时间,但是BIOS部分的耗时依然偏长。通过进一步的分析我们发现主要的时间消耗还是在VFIO 映射DMA的处理过程当中,主要有几个方面的原因:a. 映射DMA Pin内存需要逐页查询页表,开销较大;b. QEMU 存在对部分IOVA区域的反复映射及解除映射的操作。于是我们尝试在这两个方向上进行优化,通过采用批量处理的方法减少查询页表的开销,另外在QEMU中加入VFIO DMA映射区域的管理,有效的规避了效率低下的反复映射及解除映射操作,最终大幅度降低了VFIO DMA映射的时间消耗。
在解决完上述问题后我们并没有止步,对虚拟机实例创建过程中的可优化的其它地方,我们也做了相关的处理,例如关闭BIOS boot menu ,优化VFIO PCI 设备reset 的流程,去掉对GPU实例来说不必要的操作,最终将GPU实例创建过程中虚拟化部分的时间开销减少了90%以上,下面这张图展示了单卡小内存规格实例优化前后的耗时对比:
大内存规格和多GPU卡的效果更加显著,时间减少了95%以上,相关数据如下图:
经过上述的优化,目前在滴滴云上创建一个GPU实例的速度比优化前显著加快,甚至比优化前创建一个CPU实例的速度还要快,如果用户对GPU实例的创建速度有比较强的需求,欢迎到滴滴云上进行体验。
团队介绍
滴滴云平台事业群滴滴内核团队致力于为公司各种业务提供底层系统软件支撑,负责公司线上服务器Linux操作系统内核的研发并维护虚拟化相关的核心组件。团队针对各个业务的需求,在虚拟化、业务混部、资源隔离、系统性能优化等领域均有广泛深入的研究,在公有云、弹性云等业务线都有相关的产品。
作者介绍
专注于系统虚拟化研究,负责解决滴滴云底层虚拟化相关技术问题。曾就职于Intel 开源软件中心虚拟化组,具备丰富的底层系统软件开发经验。
延伸阅读
内容编辑 | Charlotte
联系我们 | DiDiTech@didiglobal.com