开场白
来阅读一下《Towards a Machine Learning-Assisted Kernel with LAKE》原论文。
o((>ω< ))o
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 | 细粒度 |
遇到的难点:
- 使用GPU/TPU会压制ML算法对性能的作用;
- 数据传输带来的摊销;
- 抽象层边界和跨层数据共享的紧张关系。
对应的解决策略:
- 使用硬件厂商提供的API,去除内核态和用户态之间的数据拷贝开销;
- 提供自定义用户接口以控制竞争,在竞争或其他性能故障发生时得以预测并回调基于CPU的处理方法;
- 基于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 负责的数据拷贝;
IOPS(Input/Output Operations Per Second)是一个用于电脑存储设备(如硬盘(HDD)、固态硬盘(SSD)或存储区域网络(SAN))性能测试的量测方式