FaaSnap: FaaS Made Fast Using Snapshot-based VMs

这篇论文对snapshot进行了大量的优化。包括

  • 使用守护进程提前拿page(concurrent paging)
  • 优化working set的locality,不是机械的按照上一次的顺序
  • 考虑linux的readahead,记录readahead拿到的page
  • 解决guest memory的anonymous跑到host memory上变成文件的问题
  • 把一些page中间的东西merge起来减少mmap的次数
  • loading set放到一个紧凑文件,这样磁盘性能就好了
  • 神奇的overlapping机制,减少mmap次数

开源,部分基于Firecracker: https://github.com/ucsdsysnet/faasnap

Observations

第一,Firecracker和Cached之间的性能差异,以及处理minor page fault(已经在page cache里了)和major page fault的时间差异,突出了在内存中缓存页面的好处。 操作系统的页面缓存在加速虚拟机页面故障方面可以发挥重要作用

第二,由于输入数据或函数执行流程的变化,一个函数的工作集会发生巨大变化。 因此,以前调用的页面访问应被视为参考,恢复快照应有效地处理工作集大不相同的调用

第三,由于guest和host之间的语义差距,guest memory中的anonymous mapping在host上变成了会引起页面故障的file mapping,这是没有必要的。

FaaSnap

System Design

FaaSnap守护程序是FaaSnap的核心系统组件。 它与Firecracker VMM进行通信,管理相关资源,并将调用任务转发给虚拟机。 FaaSnap守护进程管理本地虚拟机镜像、客户内核、快照内存和工作集文件、活动的虚拟机以及网络资源,如命名空间和虚拟网络设备。 除了客体内存的provisioning,本节描述的所有技术都在FaaSnap守护进程中实现。

FaaSnap还公开了一个API,允许远程客户端控制资源和发送调用请求。 在现实世界部署的FaaS系统中,远程客户端将是路由调用请求的负载均衡器和控制虚拟机生命周期的集群级资源管理器。

我们修改了Firecracker VMM,以配置FaaSnap的guest memory。

Concurrent paging

FaaSnap守护进程没有在等待预取完成时阻塞虚拟机,而是在设置后立即启动虚拟机。 一旦守护进程收到调用请求,它就会启动一个加载器线程,从先前调用中记录的工作集中预取页面。 FaaSnap将加载器作为守护进程中的一个线程而不是Fire-cracker VMM中的一个线程来启动, 这样当守护进程收到调用请求时,它可以立即开始预取,而不需要等待VMM开始执行。

因此,concurrent paging指的是页面故障可以由VM和FaaSnap加载器分别触发。触发之后,再访问就是minor page fault了。

Working set group

为了将磁盘读取移出虚拟机页面故障处理的关键路径,守护加载器需要在虚拟机访问页面之前预取这些页面。 理想情况下,加载器应该按照与虚拟机相似的顺序访问页面,这样加载器就有更大的机会在虚拟机之前预取到页面。

按上一次读取的顺序阅读的locality太差,linux的readahead机制就没用了。

相反,FaaSnap使用一个近似的顺序来加载。 它将工作集页面按访问顺序分为几个工作集组: 例如,前N个访问的页面被分配到组1,后N个访问的页面被分配到组2,等等。 装载器按组号递增读取组,并按顺序读取组内的页面。

Host page recording

为了确定working set group,一般都是通过触发page fault来看的。但是这样没有考虑到linux的readahead机制。 如果工作集不仅包括出错的访客页面,而且还包括由readahead缓存的主机页面,就会提高性能。 原因是,当函数输入不同,工作集改变时,readahead触及的页面可以在未来的调用中被访问。 换句话说,readahead可以 "预先决定 "一些未来的客户内存访问,即使这些页面在以前的调用中没有被触及。

FaaSnap使用mincore系统调用来构建工作集文件。 mincore扫描页表项的现有位,以确定内存范围内的页面是否存在于内存中。 在我们的例子中,它检测客户页是否在主机页缓存中。通过重复调用mincore,FaaSnap记录了自上次调用mincore以来加载的新页面。 它使用页面在mincore扫描中出现的顺序为其分配一个工作集组号。 作为一个额外的好处,mincore在记录工作集页面方面的开销比userfaultfd低,因为它不需要调用一个用户级进程来处理和记录一个页面故障。

Per-region memory mapping

为了解决guest的anonymous的map到了host上是file-based的问题。 FaaSnap没有对整个客户内存文件进行内存映射,而是只将非零的页面映射到客户内存文件中。零页则被映射到匿名的主机内存。

另一个优化是对释放的页面的处理。 一般free的内存linux就不管了,这样host还是当作非零页。 FaaSnap将被释放的页面从非零页面的集合中排除,而未来对被释放页面的访问将是快速的匿名页面故障。

Loading set

我们将加载集定义为不包括零页的工作集页面。 即使是一个简单的hello-world函数,也可能有超过1000个加载集区域,而创建大量映射的开销是不可忽视的。

然而,我们发现,在客户地址空间中相邻的许多加载集区域只被一些非加载集页(即零页或非工作集页)分开。

FaaSnap通过包括它们之间的页面来合并这些相邻区域。这种放松大大减少了需要单独映射的区域的数量,同时只增加了少量的额外数据读取量。 合并两个区域的距离阈值根据经验设定为32页,这个值可以将区域的数量减少到足够小,同时不增加太多不需要的页面。 对于hello-world,合并区域可以将区域的数量减少到100个以下,而数据总量只增加了5%。

Loading set file

分散的读取通常会导致磁盘性能降低。与REAP类似,FaaSnap将加载集存储到一个紧凑的文件中,该文件只包含加载集的页面,以便加载器更有效地读取它。

但与REAP不同的是,FaaSnap首先按组号对加载集区域进行排序,然后按地址排序。区域的文件偏移量和大小被缓存在FaaSnap守护进程中。

Summary

使用了一种神奇的overlapping memory机制来减少mmap的次数。这部分可能要看看代码了。