在使用 VFIO 透传 PCI 设备时,如果虚机使用普通的 4K 物理页,Qemu 启动速度非常慢。启动 100G 的虚机可能就要几十秒甚至更长时间。
下面我们通过火焰图来看一下普通内存的 Qemu 启动时间都消耗在哪。
$ git clone https://github.com/cobblau/FlameGraph
// 其中 25104 是 qemu 进程号
$ perf record -F 99 -p 25104 -g -- sleep 10
$ perf script | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > xx.svg
通过火焰图可以看出,时间按消耗主要在两个方面:
do_huge_pmd_anonymous_page
申请内存。clear_huge_page
对内存清零。给虚机使用巨页内存可以成功对上面第一条进行优化。
由于云虚机内存都是以 G 为颗粒度的,所以我们可以给虚机提供 1G 的巨页内存。
我们以 450G 内存的虚机为例,使用 1G 巨页内存后,启动时间可以优化到 32s。
再次通过火焰图打印一下使用巨页后的时间消耗。
可以看到使用巨页后的时间消耗的大头是在内存清零上。
如果没有使用 VFIO 透传设备,Qemu 开机时只是 mmap 出来内存并没有真正占用物理内存。真正占用物理内存是虚机在使用时产生的缺页中断中。
如果使用 VFIO 透传虚机,那么虚机的物理内存要事先分配好并且 pin 住不会让内存产生迁移。
当使用巨页内存时同样会在申请后进行预分配操作,并且在预分配内存的内核代码中进行清零操作。
从上图看到 Qemu 在预分配内存是直接写内存,然后通过内核的缺页中断实现的。
前面提到即便给虚机使用了巨页内存,虚机启动时间仍旧有 30 几秒。那么时间消耗到了哪里了呢?
原因是内核在分配内存的时候由于怕原内存中的数据遭到泄露,所以在分配时对内存进行了清零操作。
通过上面的 “巨页内存火焰图”,我们已经可以看到在缺页中断中 clear_page_erms
占据了相当大的时间,并且代码隐藏的相当隐蔽。
memset()
清零。mem_prealloc
打开,Qemu 就会创建与 VCPU 数量相当的线程用来对内存清零。可以把 memset()
的位置挪到预分配内存的函数中。clear_page_erms
函数实现换成 SSE 指令集实现。小伙伴们看看哪种方法适合自己哦。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8