泰晓科技 -- 聚焦 Linux - 追本溯源,见微知著!
网站地址:http://tinylab.org
请稍侯

QEMU VFIO 内存优化探索与实践


VFIO 内存清零过程探索
Gao Chengbo 创作于 2020/07/22

By Chengbo/高承博 of TinyLab.org Jul 06, 2020

Qemu 使用 VFIO 透传设备时启动慢

在使用 VFIO 透传 PCI 设备时,如果虚机使用普通的 4K 物理页,Qemu 启动速度非常慢。启动 100G 的虚机可能就要几十秒甚至更长时间。

通过 Perf 进行性能分析

下面我们通过火焰图来看一下普通内存的 Qemu 启动时间都消耗在哪。

通过 perf 生成火焰图

$ 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

4K 内存火焰图结果与分析

Qemu 4k 内存申请火焰图

通过火焰图可以看出,时间按消耗主要在两个方面:

  • 在缺页中断中调用 do_huge_pmd_anonymous_page 申请内存。
  • 使用 clear_huge_page 对内存清零。

使用 1G 的巨页内存进行优化

给虚机使用巨页内存可以成功对上面第一条进行优化。

由于云虚机内存都是以 G 为颗粒度的,所以我们可以给虚机提供 1G 的巨页内存。

我们以 450G 内存的虚机为例,使用 1G 巨页内存后,启动时间可以优化到 32s。

再次通过火焰图打印一下使用巨页后的时间消耗。

巨页内存火焰图结果与分析

Qemu 巨页内存申请火焰图

可以看到使用巨页后的时间消耗的大头是在内存清零上。

通过加速内存清零进行优化

Qemu 内存申请过程简介

如果没有使用 VFIO 透传设备,Qemu 开机时只是 mmap 出来内存并没有真正占用物理内存。真正占用物理内存是虚机在使用时产生的缺页中断中。

如果使用 VFIO 透传虚机,那么虚机的物理内存要事先分配好并且 pin 住不会让内存产生迁移。

当使用巨页内存时同样会在申请后进行预分配操作,并且在预分配内存的内核代码中进行清零操作。

Qemu 巨页内存申请调用流程

Qemu 巨页内存申请调用流程

从上图看到 Qemu 在预分配内存是直接写内存,然后通过内核的缺页中断实现的。

前面提到即便给虚机使用了巨页内存,虚机启动时间仍旧有 30 几秒。那么时间消耗到了哪里了呢?

原因是内核在分配内存的时候由于怕原内存中的数据遭到泄露,所以在分配时对内存进行了清零操作。

通过上面的 “巨页内存火焰图”,我们已经可以看到在缺页中断中 clear_page_erms 占据了相当大的时间,并且代码隐藏的相当隐蔽。

内核内存清零调用流程

内核内存清零调用流程

内存清零过程优化

  • 参考 2019OS2AT 大会上《腾讯云虚拟化性能优化实践》一文中蒋彪老师的思路,可以在各个 VCPU 进入 none-root 前分别对自己负责的内存地址段使用 memset() 清零。
  • 目前 Qemu 也提供了预分配内存的函数,只要预分配内存的全局变量 mem_prealloc 打开,Qemu 就会创建与 VCPU 数量相当的线程用来对内存清零。可以把 memset() 的位置挪到预分配内存的函数中。
  • 可以把内核的 clear_page_erms 函数实现换成 SSE 指令集实现。
  • 还可以让虚机在关机后,主动对内存进行清零,或者在 host 创建巨页时就对内存进行清零。

小伙伴们看看哪种方法适合自己哦。



Read Related:

Read Latest: