概述
文章目录
- 摘要
- 引言
- 2 背景
- 3 DLT的IO特性
- 4 Quiver设计
- 4.1 系统架构
- 4.2 安全模型
- 4.3 内容寻址缓存
- 4.4 Quiver服务器
- 4.5 缓存管理器
- 4.6 Quiver客户端
- 4.7 可替换命中
- 4.8 故障恢复
- 4.9 缓存服务器的局部性
- 5 缓存管理
- 5.1 协调替换
- 5.2 合作缓存未命中处理
- 5.3 收益感知的缓存放置
- 5.4 缓存共享场景
- 6 实现
- 7 评估
- 7.1 实验设置
- 7.2 使用可替换性的准确性
- 7.3 作业吞吐量的改进
- 7.4 与I/O流水线交互
- 7.5 缓存受限场景
- 7.6 收益感知缓存
- 8 相关工作
- 9 总结
摘要
- 我们介绍了Quiver,一种在GPU集群中用于深度学习训练(DLT)工作的知情存储缓存。Quiver在缓存层中采用了特定领域的智能,与通用存储缓存相比,它实现了更高的效率。首先,Quiver使用一个安全的基于哈希的寻址来透明地在操作同一个数据集的多个作业甚至多个用户上重用缓存的数据。其次,通过与深度学习框架(如PyTorch)共同设计,Quiver采用了可替换的缓存命中技术,从缓存的现有内容中获取更多的值,从而避免了当缓存容量远远小于工作集时的缓存抖动。第三,Quiver动态地将缓存优先分配给那些从缓存中获益最多的作业。通过PyTorch中的原型实现,我们表明,Quiver可以显著提高深度学习工作负载的吞吐量。
引言
你知道的越多,你需要的(缓存)就越少。
——澳大利亚的谚语
- 越来越强大的计算加速器,如速度更快的GPU和ASIC,使得存储层成为深度学习训练(DLT)工作的潜在瓶颈,因为这些工作需要足够快地输入训练数据,以保持计算单元的繁忙。例如,深度学习训练的一个流行基准是ImageNet数据上的ResNet50模型。在8个V100,训练工作可以处理10500图像/秒。由于ImageNet中的每个输入图像大约是200KB,这就意味着需要将近2GB/s的存储带宽,以保持GPU繁忙。更新、更快的硬件(例如,TPUv3、GraphCore)将进一步提高对带宽的要求,因为它们提高的计算速度要求更快地输入数据。
- 虽然这种带宽需求本身就具有挑战性,但深度学习训练(DLT)工作的三个方面加剧了这个问题。
- 首先,对于超参数调优,用户通常会运行同一任务的数十个或数百个实例,每个实例具有不同的模型配置(例如,学习率、损失函数等)。在这样的场景中,相同的底层存储必须为多个此类作业的读取提供服务,每个作业的读取都是不同的随机顺序,对存储的带宽要求明显更高。
- 其次,深度学习工作中用于输入训练数据的数据规模一直在快速增长。虽然1M的ImageNet语料库有数百GB的大小(完整的语料库要大14倍),但越来越受欢迎的新数据源要大得多。例如,视频模型中使用的youtube-8M数据集,仅帧级特征就有1.53 TB,而开放图像挑战中使用的一个子集——谷歌OpenImages数据集,完整数据的总大小约为18TB。
- 第三,运行深度学习任务最常见的模式是在云上租用GPU虚拟机(部分原因是GPU成本高);这种虚拟机的本地SSD容量有限(如,大多数Azure GPU系列虚拟机的本地SSD容量为1.5 ~ 3TB)。此外,本地SSD在跨虚拟机迁移上是临时的。与专用虚拟机相比,云提供商以更低的成本(便宜6-8倍)提供抢占式虚拟机;这些运行DLT任务的虚拟机可能在任何时候被抢占,并在不同虚拟机上从检查点恢复,丢失所有本地SSD状态。因此,用户将输入的训练数据保存在同一个数据中心区域内可靠的持久云存储中(例如,在托管磁盘或数据blob中),并从运行训练任务的GPU虚拟机远程访问该存储。从存储到计算虚拟机的出口带宽通常是一个受限的资源,特别是当多个虚拟机从同一个存储blob读取数据时。
- 在本文中,我们提出了Quiver,一个存储管理解决方案,支持上面所设置的DLT作业的I / O带宽要求,其中输入训练数据驻留在一个云存储系统,DLT工作在云中的一些GPU虚拟机上(通常是在同一个数据中心区域)。Quiver是一个用于输入训练数据的智能分布式缓存,可以在多个作业甚至多个用户之间以一种安全的方式共享,不会泄露数据。
- Quiver的关键洞见是通过利用简化存储管理的DLT工作流的几个特性从根本上提高缓存效率。首先,深度学习数据集是头部重的。一些流行的输入数据集(如ImageNet)在许多DLT作业和几个模型架构中使用。即使在公司中,团队的不同成员也可能迭代不同的替代想法,所有这些想法都针对相同的端到端问题/数据集,如web搜索。其次,每个DLT作业运行多个训练epoch(通常为50-100),每个epoch消耗整个训练数据一次(以随机排列顺序)。这两个特性使得工作负载对缓存非常友好。然而,当数据集太大,无法容纳单个虚拟机时,缓存需要以安全的方式跨多个虚拟机(可能跨组织中的多个用户)。
- Quiver利用的另一个关键观察结果是,DLT作业读取的数据是透明可替换的。DLT作业的单个epoch以随机排列顺序消耗整个训练数据,只要顺序是随机的,并且整个数据只消耗一次,那么输入被读取的确切顺序并不重要。这允许Quiver提供无抖动缓存,在可用缓存容量远远小于工作集大小的情况下,这是一种强大的技术;这种情况通常会由于抖动而导致缓存无效。相比之下,Quiver允许在访问该数据集的多个作业之间以一种分散的方式高效地缓存和共享数据集的一小部分,而不会引起抖动。
- 跨多个用户共享缓存,其中每个用户可能有私有的训练数据(可能是为了增强标准数据集),需要保护隐私,以便一个用户的训练数据不会泄露给另一个用户。隔离数据与跨共享数据重用缓存的需求冲突。为了实现这一点,Quiver对缓存进行了基于内容的安全索引,这样即使在包含相同内容的不同物理数据集(例如,ImageNet数据的多个副本/blob)中,缓存也能被重用。数据项(或一组数据项)的内容哈希被用于寻址缓存。特定用户的DLT作业提供的摘要文件包含数据集中单个数据项的哈希;拥有有效的内容哈希可以作为访问该数据项的功能,从而提供了一种简单而有效的访问控制形式,类似于内容寻址文件系统中探索的访问控制形式。
- 对于每个DLT作业,Quiver用来确定缓存放置和回收的优先级的一个重要信号是用户从缓存中获得的有效收益。DLT作业是否需要缓存主要取决于两个因素:(a)远程存储带宽(b)作业从存储中读取的每字节数据的计算量。具有非常深的模型的DLT工作将对每个输入执行大量的计算,因此即使存储带宽很低,I/O时间也可以隐藏/流水线在计算时间之后,反之亦然。此外,有些作业可能会用流水线重叠I/O和计算,而有些作业可能会同步执行I/O。因此,建模DLT作业的性能对缓存的敏感性并不是一件简单的事情。Quiver通过利用DLT作业在小批之间的可预测性来简化这一点,并使用受控探测来测量固定数量的小批(带缓存或不带缓存)的时间。这两种模式之间的性能差异是对特定DLT工作能从缓存重受益多少的精确经验度量,被用于驱逐和安置决策。
- 我们已经将Quiver实现为一个动态分布式缓存,在多个用户和运行在GPU集群中的作业之间共享,并将其集成到PyTorch深度学习工具包中。Quiver有三个组件:一个在专用用户下的独立容器中运行的缓存服务器,一个在多个缓存服务器上协调操作的缓存管理器,一个与访问缓存的每个DLT作业运行在同一个容器中的缓存客户机;事实上,客户端与PyTorch数据输入层集成在一起。这种紧密的集成允许Quiver利用特定DLT工作的复杂知识。
- 我们在一个由48个GPU组成的集群中,在跨多用户、多作业的工作负载上对Quiver进行了评估。我们演示了在混合工作负载下,Quiver将DLT作业的速度提高了3.8倍,并将整个集群吞吐量提高了2.1倍。我们还表明,Quiver的可替换缓存命中率特性避免了小缓存的缓存抖动,允许作业很好地利用部分缓存,而且受益感知的缓存优先级通过明智地分配有限的缓存空间提高了整体集群效率。
- 本文的主要贡献如下:
- 我们描述了深度学习训练作业的I/O行为,并确定了各种关键特征,如可替代性、可预测性和可共享性。
- 我们提供了第一个存储解决方案,它允许DLT作业以一种安全的方式在多个用户之间共享分布式缓存,从而为训练数据读取获得更高的有效I/O带宽。
- 我们通过挖掘有关作业对I/O性能有多敏感的复杂知识来确定和实现对缓存层的几个效率改进,并对缓存回收和放置进行优先级排序。
- 我们提供了一种新颖的防抖动缓存策略,该策略利用了DLT作业的I/O请求的可替代性,并为多个作业提供了一种以有效方式访问数据集的共享片的方法。
- 我们通过在一个由48个GPU组成的集群上的实际实施和实证评估来证明我们的技术和策略的有效性。
- 本文的其余部分结构如下。我们将在第2部分简要介绍DLT工作的背景。在第3节中,我们将从I/O的角度介绍DLT作业的各种关键特性。我们将在第4节介绍Quiver的设计,并在第5节提供关于缓存管理策略的更多细节。我们将在第6节中讨论Quiver的实现,并在第7节中评估它。最后,我们在第8节介绍相关工作,并在第9节进行总结。
2 背景
- 深度学习训练(DLT)作业将训练数据作为输入,学习一个表示训练数据的模型。为了进行学习,DLT工作采用一个小的随机样本,即每次输入项的小批(通常是32到512项),并使用随机梯度下降来缓慢学习参数,从而使预测损失最小化。每个小批都是计算密集型的(主要涉及大型矩阵/张量的乘法),并在GPU之类的加速器上运行。因为每个小批在具有相同形状的输入上运行相同的计算,所以所有小批在GPU上花费相同的时间。
- 输入训练数据: 训练数据,在高层次上,是一个元组列表,形式为 < i n p u t , l a b e l > <input,label> <input,label>,其中的输入是一个图像或语音样本或文本,将被馈送到神经网络,标签是网络应该学习分类的输入的真相值。训练大型网络,如ResNet50或GNMT,需要数百万个训练示例。例如,ImageNet-1M是一种流行的用于图像分类的训练数据,它有100万幅图像(完整数据集有1400万幅),每幅图像的大小大约为200 KB。最近的数据集,如youtube-8m和OpenImages也有几兆兆字节的大小。
- 为了以随机顺序提供输入项,DLT框架(如PyTorch)使用输入索引的概念来访问训练数据。例如,如果训练数据有一百万个项目,他们跟踪每个项目的索引列表,然后随机排列这个列表。然后,它们对存储执行随机访问,以获取与这些索引的固定数量(即小批大小)对应的数据项。当所有这些索引都耗尽时,训练的阶段就结束了,也就是说,模型已经检查了所有数据项一次。在下一个epoch,索引列表再次被随机排列,这样不同的一组小批数据就会被输入网络。DLT作业通常运行几个epoch,范围从50到200。
- 转换: 然后由DLT框架转换从存储读取的输入数据项。典型的转换包括将图像解压,从jpg格式转换为像素格式,应用各种形式的增强(缩放、旋转等)。这些转换通常是CPU密集型的。
- 多任务: 由于DLT实验的试错性质,用户经常同时运行同一模型的多个实例,每个实例具有不同的参数配置,如学习速率。每一项工作都将访问整个训练数据,但是以不同的随机顺序。
3 DLT的IO特性
- 在本节中,我们将从I/O访问的角度描述DLT作业的关键特征。
- 1. 可共享性: DLT作业在作业内部和作业之间执行的I/ O有高度的重叠。在一个作业中,由于每个作业都要对相同的输入训练数据进行多次传递(即多个epoch),因此有一个明显的好处,可以缓存数据以供以后的epoch使用。更重要的是,还有广泛的内部作业共享,原因有二。首先,通过超参数探索,多作业可能有几个作业运行同一模型的不同配置,对同一数据进行操作。这些作业可能在不同的机器上运行,但在云存储上访问相同的底层数据。其次,DLT作业使用的输入训练数据集非常头部繁重;流行的数据集(如,ImageNet)用于多种作业。即使在企业设置中,多个团队成员也在努力提高数据集(例如,web搜索点击数据)的准确性,每个成员运行不同的模型。因此有一个重要的作业间重用。
- 2. 随机访问: 虽然共享性似乎使DLT作业对缓存非常友好,但只有当整个输入的训练数据都能放进缓存时,才会这样。否则,随机访问模式(每个epoch的不同排列)使部分缓存的数据缓存不友好(事实上,相反)。如果一个缓存能够保存20%的训练数据,那么它就会因为随机的I/O而发生抖动。
- DLT训练数据的部分缓存是很重要的,因为几个数据集的训练数据已经很大了,而且只会随着模型的变大而变大。例如,视频模型中使用的youtube-8M数据集,仅帧级特征就有1.53 TB,而开放图像挑战中使用了一个子集的谷歌OpenImages数据集,完整数据的总大小约为18 TB。即使是完整的ImageNet的全部1400万张图像也有几兆兆字节的大小。此外,GPU集群中的单个服务器经常运行多个任务,甚至跨越多个任务的时间切片。由于每个作业可能访问不同的数据集,本地缓存可能跨多个数据集竞争。因此,在部分缓存下从缓存中获得有用的性能对于DLT作业非常重要。
- 3.可替换性: 幸运的是,DLT工作的另一个特性有助于解决随机访问带来的挑战。从I/O的角度来看,DLT作业的epoch只需要保持两个属性:(a) 每个输入数据项必须恰好接触一次;(b) 必须为每个小批选择随机的输入样本。有趣的是,数据项的确切顺序与作业的正确性或准确性无关,这意味着I/O是可替换的;DLT作业现在可以请求尚未访问的某个随机子集,而不是寻找特定的文件。从缓存的角度来看,这是一个独特的属性,可以极大地帮助缓存重用。有了可替换性,即使是容纳20%训练数据的小缓存也能提供良好的缓存性能,因为如果一个项不在缓存中,我们可以从缓存中返回一个保留随机性和唯一性属性的替代项。正如我们在第7节中所展示的,可替换缓存不会影响学习任务的最终准确性。
- 4. 可预见性: DLT作业的另一个有利特性是它们跨小批的可预见性。因为每个小批的时间是预先知道的,可以预测每个作业对I/O性能有多敏感,这反过来又可以允许缓存的放置和回收为从缓存中获益最多的作业提供更高的优先级。
4 Quiver设计
- 在本节中,我们将介绍Quiver的设计,这是一种分布式缓存,可以提高在GPU集群中运行DLT任务的I/O效率。Quiver是一个与DLT框架(如PyTorch或Tensorflow)紧密耦合的协同设计缓存。通过修改DLT框架,缓存客户端深入集成到DLT作业的I/O访问中,并与缓存服务器共享更丰富的信息。
4.1 系统架构
- 在了解Quiver的细节之前,我们先描述一下Quiver适合的更广泛的背景。Quiver用于组织在云上分配的GPU虚拟机上创建共享GPU集群。每个GPU虚拟机都有一定数量的本地SSD存储。DLT作业在自己的容器中运行,从而将多个用户的作业彼此隔离。更高级别的调度器和容器管理器(如Kubernetes)管理作业的提交和特定VM上DLT作业容器的调度。Quiver不知道使用的确切调度机制,也不做任何关于调度器的假设。
- 特定用户的作业的输入训练数据存储在对应用户的云存储账户中,保证隐私和访问控制;虽然一般的数据集,如ImageNet不需要这样做,但通常情况下,用户有时会使用自己的私有训练样本来增加标准数据集,这些样本可能是敏感的,或者在完全私有的数据(如监控视频,企业数据)上进行训练。VM中运行的DLT任务将在这个远程存储上执行随机读取(例如,Azure blob或Amazon S3)。
- 由于虚拟机重新部署,由于作业迁移,或者因为它运行在更便宜的可抢占虚拟机上,DLT作业可能会跨VM移动。因此,每个虚拟机的本地SSD数据都是软状态。因此,即使整个训练数据集适合于一个VM的本地SSD,将数据从远程存储复制一次到本地SSD的简单解决方案也无法工作。使用Quiver,作业可以在虚拟机之间移动,而且仍然可以透明地受益于共享的分布式缓存。
4.2 安全模型
- Quiver是一个跨多个作业和多个用户共享的缓存,因此安全语义非常重要。Quiver保证用户只能看到她在其他情况下可以访问的数据内容(即,不会在多个用户之间泄漏训练数据)。这种数据隔离需求与共享/重用缓存以获得有效性能的需求相冲突。例如,如果两个不同的用户拥有自己的ImageNet数据集副本,那么这将是两个不同的云存储帐户中的两组不同的文件,因此在逻辑上需要分别缓存,从而防止跨用户重用。Quiver使用内容寻址功能实现缓存重用,同时保持隔离。
图1. Quiver的架构。缓存服务器的运行在所有虚拟机上,但在它们自己的容器中。Quiver客户端运行在DLT作业的地址空间中,并包含对DLT框架的更改。用户的输入训练数据在缓冲未命中时从云存储中获取。每个作业的数据集被分片到多个缓存服务器上,并使用内容哈希进行查找。
4.3 内容寻址缓存
- Quiver中的缓存不是通过文件名和偏移量来寻址的,而是通过内容哈希来寻址的,类似于内容寻址的文件系统。Quiver中的缓存条目的粒度是由DLT作业决定的,它可以是单个数据项(例如,每个数据项为数百KB的图像训练数据),也可以是一组数据项(例如,文本训练数据)。为简单起见,我们在本文中假设粒度是单个数据项。对于数据集中的每个数据项,计算该项的内容哈希(例如,SHA1),生成的哈希作为缓存插入和查找的索引。内容寻址的好处是,多个副本上的相同的数据项(比如ImageNet数据在不同用户的不同存储帐户中的副本)可以映射到相同的散列,从而允许跨用户重用。
- 为了确保隔离,Quiver对输入的训练数据使用摘要文件的概念。对于用户拥有的每个数据集,用户计算每个数据项的内容哈希,并将这些哈希存储在摘要文件中。摘要文件包含形式为<content_hash: file_location>的条目,其中file_location表示特定数据项驻留在该特定用户的云存储帐户中的路径和偏移量。因此,在共享相同数据集的多个用户之间,尽管哈希组件是相同的,但每个用户在file_location组件中将有不同的条目,因为他们将指向特定用户在云中的后备存储。因为DLT作业只根据用户已经访问的数据来计算这些哈希摘要,所以哈希值的存在可以作为用户访问该内容的功能。因此,当缓存服务器获取某个哈希值的查找时,它可以安全地返回与该键相关联的数据。由于哈希函数的稀疏性及其抗碰撞特性,用户不能在没有内容的情况下制造或猜测合法的哈希。
- 由于摘要文件很小(几MB),它被本地存储在DLT作业的容器中。DLT作业首先使用哈希功能查找缓存。如果内容不在缓存中,则从远程存储中获取它(使用与哈希条目对应的file_location),然后将该内容添加到以哈希值为键的缓存中。
4.4 Quiver服务器
- Quiver中的缓存服务器是一种分布式的、点对点的服务器,运行在集群中所有的GPU虚拟机上。缓存服务器作为一个单独的特权用户(例如,组织管理员)在自己的容器中运行,所以其他运行DLT作业的用户不能访问该容器。DLT作业通过RPC接口与缓存服务器交互,用于lookup和insert。在内部,缓存服务器是维护在本地SSD上的键值存储。通过一致的哈希,键空间被多个缓存服务器实例分区;每个缓存服务器实例处理它的键空间分区。
4.5 缓存管理器
- 由于Quiver是一个分布式缓存,它需要协调收回和放置决策,以便所有缓存服务器大致同意缓存数据集的哪些部分。Quiver中的缓存管理器与Quiver客户机和Quiver服务器交互,以协调这些决策。缓存管理器还负责通过探测DLT作业来衡量每个作业从缓存中可能获得的好处。它通过指示缓存服务器临时返回由DLT作业在几个小批量中读取的所有数据的缓存未命中来实现这一点。然后,它将这个执行时间与使用缓存的正常操作期间的时间进行比较,并使用这个时间来确定缓存放置的优先级(第5节)。
4.6 Quiver客户端
- Quiver中的智能的一个重要部分存在于缓存客户机。缓存客户机作为DLT作业的一部分在用户容器中运行,位于与如PyTorch的DLT框架相同的地址空间中,并插入到DLT脚本用于访问训练数据的接口中。例如,在PyTorch中,使用
DataSet
抽象来迭代训练数据,并且它有一个简单的Next
接口来为下一个小批获取下一组输入数据项。在内部,DataSet
抽象维护一个随机排列的索引列表,该列表决定了获取数据项的顺序。Quiver对该组件进行了扩充,以同时管理哈希的摘要文件,并且当要从存储中获取一组索引时,它首先使用摘要中的哈希值在缓存中进行查找。 - 此外,Quiver客户机还将特定于作业的信息导出到缓存服务器,比如GPU上每个小批所花费的时间。这允许Quiver中的缓存服务器探测和执行有缓存和没有缓存的DLT作业的性能控制度量,并使用它来确定缓存放置的优先级。
4.7 可替换命中
- Quiver将可替换I/O的概念合并到DLT框架的数据获取组件中。现在,如果一个小批需要512个数据项,数据集加载器将提供512个要从存储中获取的索引;如果只缓存了属于索引子集的数据,则可能会丢失一些项,导致在关键路径上出现远程I/O。在Quiver中,加载器会从缓存中查找更多(例如,10倍)的索引,然后用能够缓存中获取的512个索引来适时地填满小批,这样DLT作业就可以继续进行,而不会因为缓存失败而阻塞。然后,它将缓存中遗漏的索引标记为“挂起”。数据加载器继续处理后续小批的其余索引。一旦到达列表的末尾,它就会对索引列表进行额外的传递,这一次只关注之前标记为挂起的索引。
- 为了了解为什么这样做是可行的,假设只有10%的训练数据集在缓存中(为简单起见,原始数据集的顺序连续的10%,即没有任何随机排列)。现在,因为从DLT作业中查找是一个随机排列的索引顺序,每 k k k个索引的序列都期望在 k / 10 k/10 k/10个索引中获得缓存命中;因此,如果它查找一个长度为 10 ∗ k 10*k 10∗k的序列,它可以填补大小为 k k k的小批。在第二次经过挂起条目,一个不同的,不重叠的10%的数据集可能会在缓存中,这意味着它会命中 1 / 9 1/9 1/9的查找。注意,这个属性也适用于多个作业,每个作业都有自己的随机排列。对于缓存的相同的10%,不管每个作业的排列顺序如何,每个作业预期可以命中 1 / 10 1/10 1/10的查找。因此,尽管每个任务访问的是完全不同的随机排列,但多个任务可以以全缓存命中速度进行。这样的工作负载通常会导致只包含10%数据项的小缓存发生抖动。使用可替换的缓存命中,我们可以防止抖动并提供缓存命中性能。当然,当然,这是假设一个智能缓存替换策略,我们在第5节中描述。
- 对准确性的影响: 替换命中自然会产生一个问题,那就是它是否会影响训练的准确性。正如我们在第7节跨多个模型中所显示的那样,可替代的命中并不影响工作的准确性,因为在训练数据的合理比例(例如,10%)内的随机性是足够的。
4.8 故障恢复
- 可替换性属性还有助于屏蔽缓存服务器的故障,例如由于虚拟机消失而导致的故障。在传统的缓存中,缓存服务器的故障将导致从存储中获取丢失的缓存项的不命中流量峰值。Quiver可以通过简单地返回替代数据项来优雅地处理它,同时在后台获取故障缓存服务器的内容。DLT作业不会在关键路径中产生不命中处理成本;他们只是继续使用实时缓存服务器中可用的数据;随后对索引列表的传递将使用重新填充的数据。
4.9 缓存服务器的局部性
- 虽然Quiver的简单版本(本文的重点)拥有分布在所有虚拟机上的统一缓存,但Quiver设计还允许局部性感知的缓存布局。例如,数据中心(或顶级交换机)机架内的虚拟机所使用的数据集可以只缓存在同一交换机下的其他虚拟机中,因此大多数读取都避免了跨机架交换的过载。在这样的设置中,每个机架都有自己的逻辑Quiver实例和自己的缓存管理器。因此,Quiver还可以通过减少跨机架的网络流量来帮助云提供商节省成本。
5 缓存管理
- 在本节中,我们将描述Quiver中缓存管理的各个方面。
5.1 协调替换
- 正如第4.7节中所描述的,当只有数据集的一部分(比如10%)被缓存时,Quiver会在单个epoch内对数据集的排列索引列表进行多次传递。为了在第二次传递期间获得良好的命中率,必须在第二次传递期间缓存数据集的不同部分。在多个DLT作业(例如,进行超参数探测的多个作业)访问同一数据集的场景中,这是很棘手的,因为不同的作业可能会在不同的时间耗尽它们对排列索引列表的第一次传递。
- Quiver通过为数据集的两个块分配缓存空间来处理这个问题,并使用类似于双缓冲的技术。首先,表示完整数据集的摘要文件被划分为固定数量的块,使得每个数据块占数据集的10%。数据集的分块必须智能地完成,以确保每个数据块内输入数据的随机性。一些数据集如LibriSpeech按序列长度对数据项排序;按照逻辑字节顺序对它们进行分块将导致第一个块完全由短序列组成,从而影响随机性。递归神经网络(RNN)要求小批内的所有输入具有相同的序列长度;如果一个小批包含不同序列长度的输入(如,随机选择的输入),它们填充所有输入以匹配小批中最长输入的长度。因此,为了提高计算效率,让小批中的所有输入长度大致相同是有意义的。为了在小批中高效地存储输入,我们将块定义为条带分区;让我们将输入数据集的每一个连续的10%作为一个分区。每个分区被分成10个条带单元;逻辑块就是将相应的条带单元拼接到每个分区中而形成的完整条带。为了保证序列长度的同质性,同时也确保输入的均匀分布,尽可能地,一个小批完全由单个条带单元的输入组成。
- 数据集分块允许跨多个作业协调访问缓存。当作业对第一个块进行操作时,第二个块被带入缓存,以便在(一些)作业切换到下一个传递时(可能以交错的方式)准备好。一个重要的问题是何时从缓存中取出第一个块。如果太早被赶出,仍然在第一次执行并访问该块的作业子集将不命中,而如果它在缓存中停留太长时间,则无法预加载下一个(第三个)块。Quiver使用两步流程来处理驱逐。当数据集的另一个数据块完全加载到缓存中时,一个数据块被标记为替换;现在所有的新工作都只会从最近一个块命中。然而,仍然在运行第一个块的现有作业将继续从第一个块上命中。当所有现有的作业都用尽了它们对第一个块的传递(并通知缓存服务器)时,第一个块才会实际上被驱逐。此时,可以开始对数据集的第三个块进行预加载。
- 在上面的例子中,请注意,如果一个作业比其他作业访问相同数据集的速度慢得多,那么它可能会长时间继续访问第一个块,从而防止第三个块加载到缓存中。多任务中的不同任务通常被设计成以类似的速度进行,所以这在多任务中并不常见,但可能在同一数据集上非常不同的模型中发生。有趣的是,一个作业比相同数据集上的其他作业慢得多,这意味着它每个小批在GPU上花费更多的时间,这意味着它对I/O性能不太敏感(第5.3节);缓存不命中不会太影响该作业。因此,在分块上的第一个作业完成,超过阈值时间后,Quiver会强制驱逐该分块。
5.2 合作缓存未命中处理
- 一种对存储带宽要求很高的常见工作负载是多作业,其中DLT用户在同一数据集上为同一模型运行数十个或数百个作业,但使用不同的超参数配置。如果没有Quiver,这每一个作业会从远程存储读取同一数据,导致远程存储成为瓶颈,导致每个作业的I/O吞吐量很低。Quiver使用合作未命中处理,它跨多个作业对缓存获取进行分片,以避免多个作业对同一数据项进行多次获取。这种分片是通过简单地随机化未命中文件的获取顺序来隐式完成的,从而避免了(独立的)作业之间的直接协调。因此,每个作业首先检查缓存中是否存在一组(比如2048个)数据项,然后读取这些数据项的随机子集,并将读数据项添加到缓存中。添加之后,它将执行另一个缓存查找,但这一次,它不仅会得到所添加的数据项,而且还会得到由执行类似随机获取的其他作业同时添加的其他数据项(大部分不重叠)。因此,即使在冷缓存的情况下,或者如果整个数据集不能放入缓存,Quiver也可以通过保留远程存储带宽提供好处,在单个epoch内跨多个作业读取大多数数据项。
- 在算法1中提出了一种高级的可替换缓存命中和协同未命中处理算法。
5.3 收益感知的缓存放置
- 当缓存总空间受到限制时,Quiver利用作业异构性优先将缓存空间分配给从缓存中获益最多的作业。DLT任务同时执行计算(在GPU上)和I/O。直观上,如果计算时间高于从远程存储读取的I/O时间,那么I/O时间可以重叠,无论从缓存读取还是从远程存储读取,作业性能都是相同的。然而,这是一个复杂的现象,因为它取决于作业的并行度(即它运行的GPU数量),模型有多大,模型是否以流水线计算和I/O的方式编写,等等。
- 有趣的是,与DLT框架的紧密集成使Quiver能够智能地探测和测量有缓存和没有缓存的作业性能。当一个新的作业请求将条目添加到缓存中时,缓存管理器将挑选作业进行探测。探测分为两个步骤。在第一步中,缓存管理器指示所有缓存服务器拒绝该作业的所有缓存查找,从而迫使作业从远程存储中获取数据。在这个探测阶段的最后,例如,100个小批,缓存管理器从缓存客户端(作为DLT作业的一部分运行)获得总的运行时间。然后,缓存管理器会使用默认缓存策略定期监视作业的性能。如果使用默认缓存策略和不使用缓存的时间相差不大,那么它就会得出结论,该作业在远程I/O带宽上没有遇到瓶颈,并决定关闭该作业的缓存。因此,只与此类作业接触的数据集将永远不会进入缓存,从而为其他有助于作业性能的数据集释放空间。Quiver不仅在作业开始时运行探测阶段,而且是周期性地,因为有效I/O吞吐量有可能会因为远程存储的负载增加(例如,新的作业从同一存储读取)而减少,因此使得作业对I/O性能更加敏感,反之亦然。
- 设 t h i t^i_h thi为作业 i i i在缓存命中下每小批的平均时间, t m i t^i_m tmi为作业 i i i在缓存未命中下对应的时间,则作业 i i i的缓存收益为 b i = t m i / t h i b^i =t^i_m/t^i_h bi=tmi/thi。假设 n i n^i ni为作业 i i i占用的GPU数量,则缓存作业 i i i的数据集为作业 i i i节省的GPU资源为 g i = b i ∗ n i g^i = b^i*n^i gi=bi∗ni。
- 对于每个数据集 D k D^k Dk,集群中可能有多个作业访问同一数据集。由于缓存是由所有这些作业共享的,所以如果有 N N N个作业访问 D k D^k Dk,缓存数据集所节省的GPU资源总数为 G D k = ∑ i = 0 N g i G_{D^k} = sum^N_{i=0} g^i GDk=∑i=0Ngi。有趣的是,缓存管理器必须只在三个选项中为每个数据集做决定:(a) 完全缓存(空间成本为数据集的整个大小)(b) 通过缓存一个固定大小的块(如,15G)或10%的数据集(双缓冲技术的成本是2个块)中较小的一个使能合作未命中,或©没有缓存(零成本)。注意,缓存的中间大小是没有用的,因为考虑到Quiver中可替换的缓存命中,缓存两个块的好处是一样的。
- 给定整个集群范围内的缓存空间预算S,缓存管理器使用贪婪算法优先将缓存空间分配给效益/成本比率最高的数据集或数据集块。
5.4 缓存共享场景
- Quiver透明地有利于各种DLT场景:
- 访问数据集的单个作业: 如果整个数据集都能放进缓存,那么Quiver会缓存在DLT作业的第一个epoch中访问的数据项。由于DLT作业在相同的数据上运行几个epoch,随后的epoch将从Quiver中获得完整的缓存命中,并且运行得更快。如果数据集不适合缓存,DLT作业就不会从Quiver中受益,因为它在稳定状态下从远程存储中读取数据。
- 访问单个数据集的多作业: 多作业是同一用户在同一数据集上运行的一组作业,但超参数配置不同。现在,每个作业都从远程存储中以不同的随机顺序读取相同的内容。使用Quiver,如果数据适合缓存,所有作业都会共享缓存并获得完整的缓存命中。有趣的是,即使只有10%的数据适合缓存,Quiver仍然提供了更好的性能,因为它使用合作未命中管理在作业之间对读取进行分片。
- 访问相同的数据集的不同作业: Quiver受益的另一个场景是跨作业(甚至来自多个用户)有机会共享流行的数据集。通过这样做,Quiver可以从相同的SSD空间中提取更多的价值,特别是对于流行的数据集(如ImageNet)。
6 实现
- Quiver客户端是在PyTorch 1.1.0中实现的(大约900 LOC)。Pytorch的数据模型由三个抽象组成:数据集、采样器和数据加载器。数据集返回与给定索引对应的数据项。采样器在数据集长度范围内创建索引的随机排列。数据加载器从采样器中获取一小批索引,并将它们添加到索引队列中。数据加载器的工作线程使用这些索引,并从数据集中获取数据项。为了使用Quiver,模型必须使用
QuiverDataset
(与现有Dataset
相同的接口)而不是torch.utils.Dataset
,它处理包含散列的摘要文件。类似地,模型必须从QuiverDataLoader
(与标准DataLoader
相同的接口)扩展,它在__next__
例程中探测和监视作业的小批处理进度;它还忽略传递到数据加载器API的默认采样器,而是使用自定义的采样器来处理可替换的命中,通过从数据集块采样的索引创建一个哈希列表。 - 缓存客户机使用RPC端点使用哈希来查找缓存,从缓存中获取文件,最后写入缓存并与缓存管理器通信小批处理时间。缓存未命中路径上的从Azure blob获取数据发生在常规TCP套接字连接上。QuiverDataset使用缓存客户机或blob客户机,这取决于它是在查找缓存还是填充缓存未命中。
- Quiver服务器是一个用C++编写的网络服务器,大约有1200行代码。除了用于查找/插入缓存的批接口,服务器还公开了获取当前活动块和通知“ref_chunk"和”unref_chunk"的接口;缓存客户机使用这些来协助服务器上的协调替换。服务器还公开一个接口来设置缓存模式,缓存管理器使用该接口,例如,在探测阶段禁用作业的缓存。
- 缓存管理器是一个简单的python服务器,客户机使用RPC端点来报告小批时间,它根据其效益感知的分配决策,通知缓存服务器以哪种模式缓存哪些数据集。
7 评估
- 在本节中,我们将从几个方面评估Quiver。我们在评估中回答了以下问题:
- 可替换缓存命中是否影响学习准确性?
- Quiver对不同DLT工作的加速是多少?
- 在Quiver中合作替换的效果如何?
- 在Quiver中,利益感知缓存的有效性如何?
7.1 实验设置
- 在我们的评估中,我们使用了Azure上12个虚拟机上48个GPU的集群,其中6个虚拟机每个包含4个NVidia P100 GPU,另外6个每个包含4个NVidia P40 GPU。所有虚拟机都包含3TB的本地SSD。不同的实验使用这些虚拟机的子集或全部集合。输入数据集驻留在同一个区域内的Azure存储blobs中。我们使用了一组不同的深度学习工作负载:在154 GB ImageNet数据集上的ResNet50,在531 GB OpenImages数据集上的Inception v3,在90 GB LibriSpeech数据集上的DeepSpech2。对于可替换的缓存,我们使用15GB的固定块大小。
7.2 使用可替换性的准确性
- 我们首先展示了Quiver中的可替换缓存(即,将shuffle限制为数据集的一部分而不是整个数据集)对准确性没有影响。从图2可以看出,top-1的精度曲线非常吻合。表1显示了两种配置的最终top-1和top-5精度;Quiver采样实现了与全局随机采样相同的精度。表2显示了DeepSpeech2模型在LibriSpeech数据集上的结果。同样,与全局随机抽样相比,Quiver的分块抽样收敛到类似的单词错误率。
图2. 全局随机采样和分块采样条件下ResNet50 ImageNet模型的Top-1精度。
表1. ImageNet上的ResNet50:90个epoch后的最终精度(越高越好)。两次运行的平均。
表2. LibriSpeech上的DeepSpeech2的精度:最终WER(越低越好)。两次运行的平均(30个epoch)。
7.3 作业吞吐量的改进
- 我们现在评估Quiver在三种不同工作负载上的性能提升:ResNet50、Inception和DeepSpeech2。在每个工作负载中,我们在28个GPU上运行一个多作业。回想一下,一个多作业运行同一个模型/作业的多个超参数配置。对于每个多任务,我们运行7个作业(不同配置),每个任务在单个VM中的4个GPU上运行。我们展示了三种配置下多个作业的总吞吐量(小批/秒)。
- 基线配置,其中所有作业都从远程存储blob读取。该配置在图中称为"Cache miss"。
- 在Quiver中,所有的数据读取都会导致缓存命中。这是使用Quiver时的最佳性能,并在图中显示为"Cache hit";
- 当Quiver从冷缓存开始时,DLT作业执行协作式缓存不命中处理,以避免远程存储上的冗余I/O。这也表示当数据集只有10%或20%的片被缓存时的性能(第4.7节)。
- 图3显示了这三种工作负载的结果。可以看出,"cache hit"曲线的斜率始终比"cache miss"曲线的斜率小得多。换句话说,相同数量的小批使用Quiver可以更快地处理,从而提高效率。"co-operative miss"曲线位于cache hit和cache miss配置之间。因此,即使从冷缓存开始,Quiver避免从所有7个虚拟机到远程存储的冗余I/O的能力允许它从远程存储中提取更高的有用带宽,从而提高效率。有趣的是,在图3©中,合作未命中和缓存命中之间的差别很小,这表明工作负载仅使用一小片缓存(第5.3节)就可以运行得同样快。表3显示了Quiver为这三种工作负载实现的总体加速。
图3. 在三种模型(ResNet50, Inception v3,和DeepSpeech2)上的使用Quiver的7个作业的多作业的进度时间表。每个作业在单个VM内的4个GPU上运行。
表3. 在三种工作负载下从Quiver获得的加速。
7.4 与I/O流水线交互
- 包括PyTorch在内的DLT框架流水线I/O计算和计算,以隐藏I/O延迟。特别是,数据加载器维护一个由获取的小批输入组成的队列,而计算引擎从内存队列中选择。基线和Quiver都从流水线中获益,所以前一小节中显示的Quiver的好处是加在流水线之上的。现在,我们分析小批中多个流水线阶段的时间分解,以了解由于缓存命中而产生的更快的I/O如何提高作业性能。为此,我们在VM中的4个GPU上放大单个ResNet50作业的20个小批。
- 图4是一个甘特图,显示了从远程I/O读取数据时ResNet50作业执行的微时间轴(20个连续的小批量,每个处理512张图像)。x轴表示时间,y轴表示从训练中随机的一个小批开始,从1到20的小批下标。每个条的三个盒子属于小批处理的三个主要阶段:对应于(从远程存储或Quiver)读取输入的I / O,变换对应在输入上执行转换的Transform,如图像增强(CPU密集型),GPU是在GPU上的实际的计算。理想情况下,GPU是最昂贵的资源,一定不能闲置。然而,对于远程I/O, GPU大部分时间是空闲的(从小批
i
i
i和小批
(
i
+
1
)
(i+1)
(i+1)的GPU阶段之间的差距可以看出),因为I/O时间限制了作业进度。图5显示了Quiver缓存命中下的微时间轴。可以看到,GPU在这种设置中几乎得到了充分的利用,因为I/O完成得更快。尽管在基线和Quiver中,每个小批的数据转换都需要很长时间,但由于它是跨多个CPU核上的多个小批的并行处理(由于流水线),所以它不会影响Quiver中的GPU利用率。因此,尽管两种情况都得益于PyTorch中的I/O流水线,但Quiver能够更好地隐藏I/O延迟。
图4. 在远程I/O下,ResNet50的20个连续的小批的详细时间线(不同阶段)。
图5. 在Quiver命中下ResNet50的连续20个小批的详细时间轴(不同阶段)。
7.5 缓存受限场景
- 在这个实验中,我们运行4个ResNet50作业(每个作业在单个VM中的4个GPU上)访问相同的ImageNet数据集。大约15分钟后,我们在另外3个虚拟机中启动另外3个ResNet50作业。我们限制缓存空间只能容纳20%的输入数据。这导致Quiver将训练数据切成10个块,并同时使用两个块执行双缓冲(第4.7节)。图7显示了这些作业的总吞吐量。图中的每一条垂直线表示特定块在Quiver缓存中驻留的时间(最上面的x轴表示块的数量)。
图7. 多个作业共享一小片缓存的协同替换。
- 从这张图可以看出几个方面。首先,如果在任意时间t画一条平行于x轴的线来分割图形,它就表示当时Quiver缓存的块的数量,只需计算相交的垂直线的数量。可以看到,在任何给定的时间,只有2块实际驻留在缓存中,这表明协同替换。第二,在总吞吐量中,我们可以看到在实验进行到大约15分钟时(即,当作业数量从4增加到7时),进度率增加了,因为现在有更多的作业参与了协同未命中处理,从而提高了每个作业的吞吐量。最后,可以注意到,当三个作业开始时(大约t=900秒),缓存的前两个块已经被删除了。尽管如此,这些工作能仍然够取得良好进展,因为它们执行可替换缓存,但首先从第三个块开始(而前4个作业是从第一个块开始的)。这种动态可替换性是由缓存管理策略确保的,该策略将新作业定向到当前活动的块,以便清除其他作业已经耗尽的旧块。
7.6 收益感知缓存
- 在这个实验中,我们展示了在Quiver中的收益感知缓存的有效性,并将其与一个简单的基于LRU的缓存替换策略进行了比较。为此,我们在48个GPU上运行一个混合了三种不同DLT模型的工作负载。我们分别运行ResNet50、Inception和DeepSpeech的四个作业,每个作业需要一个带4个GPU的VM。正如我们前面在图3中看到的,这三种作业从缓存中获得的好处是不同的。这些作业分别使用三个数据集:ImageNet、OpenImages和LibriSpeech。
- 图6显示了标准化集群吞吐量的稳定状态时间轴(缓存预热后大约1000秒)。为了量化相对集群吞吐量,我们计算所有12个作业的作业进度率(处理的小批)与基线(没有缓存)配置相比的相对改进。我们展示了不同缓存大小下的集群吞吐量:无缓存、100 GB缓存、200 GB缓存和700 GB缓存。注意,这三个数据集的完整大小大约是780 GB。由于700GB的配置接近完整数据集的大小,所以LRU的性能接近于Quiver。然而,对于剩余的80GB, LRU的吞吐量只比基线高2.2倍,而Quiver是2.32倍。
图6. 在48个GPU上使用Quiver、基本LRU和没有缓存(基线)同时进行多个任务的集群GPU吞吐量。工作负载由ResNet50、Inception和DeepSpeech2的4个作业组成。这12个作业都在4个GPU上运行,总共使用48个GPU。将显示归一化到非缓存场景的平均集群吞吐量。
- 更有趣的是,当缓存大小远低于数据集的总和大小时,Quiver在更受限的缓存场景下的性能。在这些配置(100 GB和200 GB)中,Quiver能够基于其动态的小批感知探测(第5.3节)智能地分配缓存空间,此外还使用合作的未命中处理和替代命中来提高吞吐量。对于100G,它对所有三个数据集使用协同未命中,使用15GB的固定分块大小(三个数据集的双缓冲总共大约90GB)。在200GB的缓存中,Quiver自动选择完全缓存ImageNet数据集(因为ResNet50从缓存中受益最多),而对其他两个执行合作未命中。在700G缓存,它缓存ImageNet和OpenImages数据集。Quiver能够优先为受益最大的作业分配缓存空间,从而最大限度地提高集群吞吐量。在这两种配置中,LRU的性能都比Quiver差,因为DLT作业的随机访问模式导致了LRU的抖动。总的来说,即使使用很小的缓存(100G), Quiver仍然能带来1.6倍左右的可观收益;根据缓存大小,整个集群吞吐量的提升范围在1.6倍到2.3倍之间。
8 相关工作
- 提高DLT作业的I/O性能最近受到了一些关注DeepIO探索了使用内存缓存进行I/O读取的流水线计算和熵感知采样技术的使用。DeepIO孤立地看待单个DLT工作;缓存单个作业的好处很少,除非整个数据都能放进缓存,因为单个DLT作业的工人在每个epoch中只读取每个数据项一次。相比之下,Quiver可以安全地实现跨多个作业的缓存重用。因此,即使只有一小部分数据可以放入缓存中,它也可以通过使用可替换命中和协作未命中处理来协调多个作业的I/O访问,从而提高性能。Quiver在其放置上也是收益感知的,因此节省使用缓存,优先处理最有利的工作。正如DeepIO的作者所指出的,DeepIO在部分缓存场景中的(适度的)好处是通过使用RDMA 洗牌减少了复制开销和线程调度成本;相比之下,Quiver实际上通过使用协同未命中管理减少了等待I/O的时间。
- 分析社区已经对集群环境中的分布式缓存进行了更广泛的研究。例如,PacMan探索了在分析工作的不同工人之间协调的数据缓存,以获取查询性能的最大收益。类似地,Quartet探索了大数据作业的智能调度,以最大程度地跨作业共享缓存数据。Quiver中的协同驱逐策略与这些工作有一些相似之处,但处理部分数据缓存而不抖动的能力是Quiver独有的,因为这只能是因为DLT作业的可替代性。EC-cache是一个分布式集群缓存,在读取过程中使用纠删码实现动态负载均衡。由于DLT工作负载的规律性,在Quiver中简单的静态分区似乎就足够了。在大数据世界中,还有其他关于各种形式缓存的工作。
- Quiver也与深度学习系统最近的工作有关,它使用DLT作业的可预测性来提高效率。Gandiva使用跨小批的可预见性来检查作业性能,并使用它在GPU之间迁移作业,或将作业紧密地打包在同一个GPU中。Astra利用小批的可预测性,通过在线分析多种优化选择来执行动态编译和DLT作业的优化。Quiver利用了类似的观点,但使用了可预测性来智能缓存优先级排序和预取。
- 文献也研究了如何通过应用程序提供的提示来做出缓存和预取决策。与应用程序无关的通用提示既具有挑战性又具有局限性;通过为DLT作业构建垂直集成的、特定于域的缓存,Quiver中的接口既简单又强大。合作缓存也得到了研究;与过去的工作不同,Quiver使用可替换缓存和协调逐出来管理部分工作集。
- Quiver中基于内容哈希的寻址基于使用哈希作为功能的概念;类似的方法已经在内容索引文件系统中探索过。Quiver将此思想应用到共享缓存的上下文中,以同时提供数据隔离和缓存重用。
9 总结
- 在过去的几年里,深度学习已经成为一个重要的系统工作负载:深度学习加速器的硬件初创公司的数量证明了它的普及。深度学习系统主要关注于提高计算效率和网络效率,存储层主要由特别的解决方案来处理,例如将数据手动分段到本地SSD,这有很大的局限性。借助Quiver,我们提供了一种自动缓存机制,在深度学习不断增长的计算能力面前,帮助弥补存储性能的差距。Quiver通过与深度学习工作流和框架紧密集成来实现缓存效率,并利用I/O可替换性等特性来确保高效缓存,即使在缓存中只能容纳数据的子集时也是如此。
最后
以上就是外向眼睛为你收集整理的【FAST’20】Quiver:深度学习的知情存储缓存摘要引言2 背景3 DLT的IO特性4 Quiver设计5 缓存管理6 实现7 评估8 相关工作9 总结的全部内容,希望文章能够帮你解决【FAST’20】Quiver:深度学习的知情存储缓存摘要引言2 背景3 DLT的IO特性4 Quiver设计5 缓存管理6 实现7 评估8 相关工作9 总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复