比较虚拟机中的文件系统性能
多年来,使用 Vagrant 的基于虚拟机的开发环境,其主要瓶颈一直是文件系统性能。CPU 方面的差异很小,几乎难以察觉;只有在同时运行许多虚拟机时,RAM 才会成为问题。
昨天我花了大半天时间,对常见的文件系统机制进行了基准测试和分析,现在将结果分享给大家。
我会先分析结果,因为这对大多数人来说是最有趣的部分。具体的测试方法、所使用的软件,以及我的测试原始数据,都可以在本文分析内容的下方找到。
在下面的每张图表中,我们都会以某种方式测试读取或写入文件。每张图中写入文件的总大小都是固定的。Y 轴表示吞吐量,单位为 KB/s。X 轴表示“记录大小”,即每次读取/写入的数据块大小,单位为 KB。
不同的测试环境包括 native、VirtualBox native、VMware native、VirtualBox shared folders (vboxsf)、VMware shared folders (vmhgfs) 和 NFS。“native”环境指的是在相应环境中直接使用文件系统本身。“native”是在主机上,VirtualBox native 是 VirtualBox 虚拟机中的根设备,其他情况以此类推。NFS 只在 VirtualBox 上进行了测试,因为它在 VirtualBox 和 VMware 中的性能特征应该相近。
对于每张图表,吞吐量(Y 轴)越高越好。
小文件顺序读取
首先测试的是对一个 64 KB 文件进行顺序读取,并使用不同的读取记录大小。小文件顺序读取的一个实际应用场景,是加载应用程序的源文件以便运行、编译或测试。

首先你不得不注意到,对于较小的记录大小,NFS 的读取性能惊人地高。NFS 很可能执行了大量的 read-ahead(预读)操作和缓存,才取得了这样的性能。我无法很好地解释 NFS 为何能胜过原生虚拟文件系统。
VMware shared folders 在这里完全碾压了 VirtualBox shared folders。VirtualBox shared folder 的读取性能糟糕透顶。如果查看原始数据,就会发现其吞吐量实际上从未超过 100 MB/s;而 VMware 从未低于 500 MB/s,峰值更超过了 900 MB/s。
有趣的是,在某些情况下,虚拟机内部的原生文件系统性能会超过主机上的原生文件系统。这个测试使用的是原始的 read 系统调用,没有任何用户空间缓冲。虚拟机监控程序很可能会对来自虚拟机的读取进行缓冲,因此由于不必频繁地切换上下文到原生内核,它们能够获得更好的性能。查看 fread 基准测试的原始结果数据,可以进一步支持这一理论。在那些测试中,原生文件系统每次都胜过虚拟文件系统。
大文件随机读取
本项测试以不同的读取记录大小,随机读取一个 64 MB 文件中的不同部分,测试吞吐量。这个文件比上一个测试中的文件大 1000 倍。当处理命中文件系统的数据库读取时,可能会出现这种行为。

与小文件顺序读取相比,VMware shared folders 和 VirtualBox shared folders 之间的差距显著扩大了。VirtualBox 的表现如此糟糕,以至于你几乎看不见它。再次强调,VirtualBox 的吞吐量峰值从未超过 100 MB/s。另一方面,VMware 的峰值达到了 7 GB/s。由于在不同测试用例中 VirtualBox 的吞吐量变化非常小,我推测 VirtualBox shared folder 系统中存在一条限制性能的单一代码热点路径。他们显然做错了什么。
NFS 的优势不再那么明显,这很可能是因为本测试用例无法体现预读带来的好处。不过,与其他选项相比,NFS 的表现仍然非常好。
和小文件顺序读取一样,我们仍然看到虚拟机内部的性能优于虚拟机外部。同样,这可以归因于虚拟机监控程序进行了巧妙的缓冲,而对主机执行的原始系统调用无法做到这一点。
小文件顺序写入
下面来看看小文件顺序写入的性能。这最准确地描述了保存会话状态、临时文件或写入新源文件的场景。

首先明显可以看到,对于这类写入,NFS 的性能非常糟糕。NFS 在这里实际上无法进行缓存,因此你必须承受网络开销的全部代价,然后还要在主机端写入磁盘,最后再次等待虚拟机确认写入成功。真够呛。
各种“native”文件系统的表现都非常好。虚拟机再次超过了主机。同样,这可以归因于虚拟机监控程序中的缓冲。
几种共享文件系统的表现相近,但在这个测试用例中,VirtualBox 明显胜过 VMware。
大文件随机写入
最后一张图表展示的是向一个较大(64 MB)文件进行随机写入的测试结果。和大文件随机读取测试一样,这是测试数据库性能的一个好方法。

与小文件顺序写入相比,这里的差异并不大。由于文件较大,不同测试环境之间的差距也更大,但除此之外,结果基本相同。
NFS 的写入性能依然糟糕。VirtualBox shared folders 的写入性能继续超过 VMware,而虚拟机监控程序的表现也继续超过主机。
虚拟机监控程序超过主机,是我认为最有趣的地方。这个测试的结果清楚表明,为了性能,虚拟机监控程序一定在对同步写入进行不实报告。这也印证了我在使用 Packer 时看到的情况:如果虚拟机没有正常关机,已提交的写入就会丢失。虚拟机中的 fsync() 并不意味着数据已经写入主机,只意味着数据已在虚拟机监控程序内部提交。
总体分析
就共享文件系统而言,VMware 的行为符合你的需求。加载网页、运行测试套件和编译软件都非常偏重读取。VMware shared folder 的读取性能远胜 VirtualBox,而 VirtualBox shared folders 的写入性能也只比 VMware 略好一些。
如果你可以选择使用 NFS,那就使用它。再强调一次,读取性能远比写入性能更有价值。
虚拟机监控程序的读写性能非常出色(因为它们作弊了)。得益于这些数据,我肯定会更加关注 Vagrant 中仅使用原生文件系统的新同步文件夹实现(例如 rsync,或者让主机作为 NFS 客户端而非服务器)。
不过,就更直接适用的建议而言:如果你使用虚拟机进行开发,并且条件允许,请将数据库文件移出共享文件系统。你很可能会看到巨大的性能提升。
最后,我不认为这些结果中有什么特别令人意外的地方。Vagrant 从 2010 年起就支持 NFS 同步文件夹,因为我们很早就意识到共享文件夹的性能很差。不过,拥有一些能够展示不同表现的硬数据总是件好事,也能让我们有趣地了解每个系统可能在做什么。
随机一篇博客