『机器学习』MLSys提前看 | 机器学习的分布式优化方法( 六 )


2、快速任务切换
首先 , 作者对于 DL 任务中内存分配的类型进行分析 。

  • 模型:这些主要保存模型参数 , 通常由一些大的内存块组成 。 由于模型大小在整个训练过程中通常是固定的 , 因此模型数据很少或没有时间变化 , 并且是可预测的 。
  • 短暂:这些是每次迭代所需的临时内存 。 这些内存通常保存中间层的输出以及算法本身生成的临时数据 。 它们只在计算期间才需要 , 并且在迭代之间被释放 , 从而产生 DL 作业的时间内存使用模式 。 它们通常也是大内存分配 。
  • 框架:这些通常被 DL 框架用于记账或数据准备通道 。 它们经常在迭代中持续存在 。
由上述分析可知 , 模型和框架类的内存需求在迭代过程中是一直存在的 。 此外 , 对于 DL 作业 , 持久内存使用率明显低于临时内存 。 有可能在 GPU 中保留多个作业的持久内存 , 同时仍有足够的空间存储任一作业的短暂内存 。 由此 , 作者得出结论:不从 GPU 中删除持久内存就可以实现快速的作业切换 。
考虑到迭代在 DL 作业中通常很短(从几十毫秒到几秒不等) , 而且粒度更细 , 例如在 GPU 内核级别 , 因此可以进一步利用 GPU 资源 。 但是 , 细粒度的调度也会给执行服务增加更多的开销 。 因此 , 对于给定的调度粒度 , 需要在最大利用率和效率之间寻找最优的折衷 。 考虑下面的场景 , 给定 12GB 的 GPU 内存容量 , 作业 A 和作业 B 进行了两次迭代 。 它们的模型内存使用量是 PA=PB=1GB , 而临时内存使用量是 EA=EB=7GB(由于框架相对较小 , 忽略了它的内部使用量) 。 内存的分配方式不是一次分配所有 8GB , 而是一个作业的每个迭代以不同的增量分配 。 在迭代之间切换的选择允许回避渐进式内存分配的问题 。 这是因为框架在每次迭代后都会释放所有短暂的分配 , 并且模型和框架内部分配在整个迭代中保持不变 。
3、内存共享
在高效作业交换的基础上 , 作者设计了一种特殊的内存布局方案 GPU 通道(Lane) , 实现了内存共享 , 从而提高了内存利用率 。 首先 , 将 GPU 内存空间分为短暂的和持续的区域 。 「模型」和「框架」分配持续区域 , 而「短暂」则分配的是短暂的区域 。 短暂区域进一步划分为通道 , 通道是连续的内存空间 , 其中可以包含用于迭代的短暂内存分配 。 然而 , 通道又不仅仅是关于内存 。 实现通道内串行的迭代过程以及基于 GPU 流的通道间并行处理 , 其中 , 每条通道都可以分配给多个 DL 任务 。
首先 , 通过实现一个能感知应用程序的箱式内存分配器 (application-aware bin-based memory allocator) 来解决这个问题 , 以减少内存碎片 。 这种方法打破了通常在 DL 框架中使用的内存优化 , 因为它们假设一次运行一个作业 。 将短暂区域划分为通道并不能消除内存碎片 , 它会将通道内的碎片移动到通道级别的碎片 , 只不过解决通道级别的碎片化要相对容易一些 。 在通道的情况下 , 所分配的内存在每次迭代结束时完全释放 , 并在下一次迭代开始时返回 , 毕竟它们是短暂的内存 。 因此 , 碎片整理几乎是自动进行的 , 无需任何成本 , 也不需要额外的内存移动 。
在进行通道内存分配的过程中 , 始终保持满足下式:

『机器学习』MLSys提前看 | 机器学习的分布式优化方法
本文插图

其中 , Pi 和 Ei 分别是任务 i 的持久性内存(模型和框架内部)和短暂内存 , Lj 表示通道 j 的通道大小 , 同时也被定义为通道中所有工作的最大短暂内存使用数 。 C 是 GPU 的容量 。 Salus 通过确保所有允许的作业都分配有足够的持久内存容量 , 并为具有最大临时内存需求的迭代保留足够的内存 , 提高了内存的利用率 , 同时也确保了通道中至少有一个作业可以继续 。


推荐阅读