少女祈祷中...

开场白

来阅读一下《Towards a Machine Learning-Assisted Kernel with LAKE》原论文。

o((>ω< ))o

论文链接:https://dl.acm.org/doi/abs/10.1145/3575693.3575697

LAKE

Learning-assisted, Accelerated KErnel

5个启发式内核子系统可以被ML加速:(最后一行是文件系统加密(EFS))

应用 ML算法 API 推理频次
存储系统里I/O延迟的预测 神经网络 CUDA 细粒度
预测缓存中的页热度以替换 LSTM High level 粗粒度
预测CPU之间工作窃取的收益 神经网络 CUDA 细粒度
预测下一个I/O访问以预读 神经网络 CUDA 粗粒度
监测恶意软件和攻击 k-NN CUDA 粗粒度
文件系统中从storage中放入/取出过程中的加密/解密 - CUDA 细粒度

遇到的难点:

  1. 使用GPU/TPU会压制ML算法对性能的作用;
  2. 数据传输带来的摊销;
  3. 抽象层边界和跨层数据共享的紧张关系。

对应的解决策略:

  1. 使用硬件厂商提供的API,去除内核态和用户态之间的数据拷贝开销;
  2. 提供自定义用户接口以控制竞争,在竞争或其他性能故障发生时得以预测并回调基于CPU的处理方法;
  3. 基于API,在内核里记录子系统有关训练和推理的数据,以预测困难(比如异步问题和抽象层边界问题)。

论文贡献:

  • 一套使得针对ML硬件加速子能在内核中使用,提供界面解决在内核和用户共享加速子情况下、竞争和可变的性能收益率问题。
  • 在不同内核子系统下,简化特征收集和特征处理的高性能的API
  • CPU利用率降低和内核子系统性能提高的评估

加速子的栈并不在内核空间里可见,通常还依赖于kernel-bypass设计,这种设计将专有的高级API支持纳入用户空间。现有的加速子虚拟化技术并不充分,而且内核和用户空间的数据传输也低效率、不可行。

最关键的挑战在于:

  • 处理内核和用户之间对加速子之间的竞争
  • 减少不必要的内核/用户边界上的数据移动
  • 让内核子系统能够调整CPU和加速子,依据性能和准确的收益率

设计

三个重要组件:

  • 内核侧API提供者 lakeLib
  • 内核-用户通信通道 lakeShm
  • 用以实现API的用户侧守护进程 lakeD

lakeLib是一个kernel module,将加速子厂商提供在用户空间的API暴露到内核空间。暴露前后的API采用同一个名字。内核里的每个API做三件事:

  • 序列化API标识符和参数进入到一个指令
  • 发送指令通过通信信道到用户空间执行
  • 等待回复

lakeD在用户侧监听上述指令,反序列化并执行需要的API

lakeShm是一个kernel module,提供内存分配的API,方便内核-用户通信。它通过请求内存并把内存映射到内核空间的方式工作。

将内核中使用LAKE的操作分为以下三类:

  • local operations: 内核已有的功能,不被LAKE改变,比如内存分配vmalloc
  • API-remoted operations
  • copiable memory allocations

CUDA提供用户空间到GPU的零拷贝发送的API,但LAKE做不到因为CUDA的library是闭源的。所以使用lakeShm本身不会在内核空间和加速子之间产生零拷贝数据传输。数据拷贝的开销需要使用LAKE提供的更高层次的API来消除。

存在一个batch size,CPU和GPU的处理效率相当(CPU适合小batch,GPU适合大batch),于是LAKE支持在运行中这两者使用上的切换。(这样依据实时效率的切换成为了一个机制policy)

上面这个机制同样也解决了竞争问题,当GPU被占用时也将计算撤回CPU中。

像Tensorflow这些现有的ML库将复杂的机器学习功能做成了更高层次的API,直接妨碍到了使用CUDA的runtime API。同时又很难去实现CUDA算法或者Tensorflow这些库的优化,于是必须要找到机制去让LAKE可以使用这些高层次的库。

LAKE的API很通用以支持手动添加API。

实现

LAKE支持内核的登记 registry

Feature vectors存储在内存中,格式为<numfeatures, kvpair*, ts_begin, ts_end>

kvpair* 是一个map,保存key-value键值对

每个registry有一个schema纲要,实现上是一个map,key为特征名字,键值为元组<size, entries>,size是特征类型的字节大小,entries提供一个数组数量,该数组记录该特征的历史值。

LAKE不跟踪真实的特征的向量项,而是把values当作无类型的字节处理。对于多数的特征类型(比如int),entries为1,表示该向量就是个标量。当entries大于1时,就是数组的长度,此时下标越小表示越新。

精准控制batch size是使用加速子的关键参数。通过传递参数 时间戳ts 返回部分特征信息。

实现细节

通信通道采用Netlink sockets实现。

lakeShm保留了DMA区域,内存分配采用最佳匹配。使用内存映射避免kernel-userspace之间巨大的数据缓存和传输。

lakeD通过维持独立的地址空间、沙箱空间、Seccomp(安全计算(Secure Computing)模式)来保证安全性。

测试

结合各个内核子系统进行性能评估。

存储系统里I/O延迟的预测

可预测的延迟对于服务于交互式应用程序(如消息传递和搜索)的数据中心系统非常有用,如果系统预测某个I/O将变慢,那么可以通过向另一个存储节点发出重复的I/O请求来减轻延迟损失。

LinnOS展示了OS如何通过神经网络学习并推理每次I/O的延迟。它使用一个基于系统的阈值,将延迟分类为慢和快。

在LAKE的框架下尝试增加了LinnOS的层数。

基本上只有在神经网络层数多的情况下LAKE才有明显的延迟降低。

相关

mmap是Linux的内存映射函数,将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。

也是一种零拷贝技术,用户空间的 mmap file 使用虚拟内存,实际上并不占据物理内存,只有在内核空间的 kernel buffer cache 才占据实际的物理内存。

mmap 仅仅能够避免内核空间到用户空间的全程 CPU 负责的数据拷贝,但是内核空间内部还是需要全程 CPU 负责的数据拷贝;

https://zhuanlan.zhihu.com/p/507907660

IOPS(Input/Output Operations Per Second)是一个用于电脑存储设备(如硬盘(HDD)、固态硬盘(SSD)或存储区域网络(SAN))性能测试的量测方式