资源依赖的“诅咒” | 原有深度学习框架的缺陷①
source link: https://my.oschina.net/oneflow/blog/5088715
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
撰文 | 袁进辉
1 算子之间的三种依赖关系
无论是动态执行还是静态执行,算子(op)之间的依赖关系都可以用数据流图来描述。一般来说,算子之间产生依赖关系的原因有以下三种: 数据依赖 :消费者对生产者的依赖,因生产-消费关系传递而产生的依赖,譬如算子A-->B-->C 表示B需要使用A生成的数据,C需要使用B生成的数据,实际上C依赖于A,但A和C之间并不需要有一条边显式的描述这个依赖关系; 控制依赖 :两个原本没有生产和消费关系的op A和B,因某种需求而希望他们按特定顺序发生,可以通过在这两个op之间引入控制边(control edge),A-->B以确保B永远在A后面发生; 共享资源形成的依赖 :两个原本没有生产和消费关系的op A 和B,因共享某一个互斥的资源而导致二者不可能同时发生所形成的依赖,要么A先于B发生,要么B先于A发生; 值得注意的是,原有的主流深度学习框架的调度器对前两种类型的依赖关系都有显式的描述和处理,但无一例外都忽视了第三种依赖关系的处理。 本文用一个简单的例子来论证:- 忽略因共享资源而形成的依赖是深度学习框架设计上的致命不足,这个问题会降低系统的安全性和稳定性;
- 在现有框架的设计体系内解决这一问题不是不可能,但难度比较大,且会破坏系统抽象的优雅性;
- OneFlow中基于actor机制可以比较简单的解决这个问题。
如果 O2 先被调度,它可以成功执行并消费内存中 M2 的输出。 O2 完成之后, O2 的输入和输出就可以被释放 (状态4)。然后 O1 就可以被调度并成功执行。
如果是O1先被调度,那么剩余内存对O1来说是不够的,但调度器在发射O1指令的时刻并不关心也不知道当前的内存是否能满足O1的需求。
3
借助控制边规定算子的执行顺序并非易事
通过添加控制边来规定算子的顺序的前提是,找到一个在资源约束下能成功执行且最优的执行顺序,这本身就很困难。下面,让我们考虑深度学习框架里常见的数据加载流水线的例子来说明这个问题。图2. 流水线设计:横向表示时间,纵向自上而下表示流水线的几个阶段,数据加载、预处理、copyh2d和计算(其中计算环节是瓶颈) 图2说明了深度学习框架中常见的通过流水线设计来重叠数据加载、预处理、copyh2d(即把数据从主机内存复制到设备内存)和计算的过程,采用双缓冲技术来实现这种流水线。每个算子都有2个单元的内存配合,蓝色和灰色表示有数据占用,而空白表示内存处于空闲。 如图所示,在同一批次(例如batch 6) 中,下游算子依赖于上游算子(例如,batch 6),preprocess依赖于data loader,而copyh2d依赖于preprocess,compute依赖于copyh2d。当流水线进入稳定阶段时,背压机制使得其他算子均以耗时最长的算子的速度执行(即图中的“计算”过程)。 这就导致了一些反直觉的依赖关系(如虚线所示),即新批次上的上游算子依赖于老批次的下游算子。例如,copyh2d的batch 4的执行依赖于computation的batch 2。这种流水线效应很难通过用控制边指定执行顺序来完成。 原有框架的运行时和调度机制很难(如果不是不可能的话)优雅地支持这种需求,所以现有框架在核心图执行器(graph executor)之外借助另外的模块来支持这种流水线功能,例如Nvidia DALI和TensorFlow中的dataset API。
4 通过定制内存分配器支持双缓冲流水线
你可能会好奇,是否有可能用现有框架的核心机制来实现双缓冲?图3就展示了在TensorFlow里实现双缓冲的一个潜在方案,也就是添加一个自定义的内存分配器,这个内存分配器限制每个算子只能分配到两份内存。 图3. 自定义分配器,支持双缓冲
当一个算子使用分配器分配内存时,分配器会查询一个计数器,查询该算子是否有空闲的缓冲区可以使用。如果有,它就为该算子分配一个缓冲区,让该算子继续执行,并在该算子完成计算后释放缓冲区。如果这个算子的两个缓冲区都已被占用,分配器会将步骤2 (do compute)和步骤3 (release) 放入一个等待列表中。
- 当一个算子向分配器释放其内存时,分配器更新与该算子对应的空闲缓冲区的计数器,并检查等待列表中是否有请求该缓冲区的算子。如果有,就从等待列表中弹出处于就绪状态的算子,执行步骤2和步骤3。
5
如何优雅解决资源依赖问题?
上述思想实验所揭示的麻烦的根源是,原有框架没有考虑共享资源引入的隐式依赖,没有显式的表达这种依赖关系,把成功执行完全寄希望于运气,也就是“运行时”的动态状况,只不过大部分情况都能成功执行罢了。 具体到工程实现,现有的框架的调度器只要看到一个op的输入数据就绪,就认为这个op可以“发射”出去,在发射出去后执行时才会去为这个op的输出数据分配内存,这个分配内存的逻辑和op的计算逻辑没有分开。 OneFlow的与众不同之处在于,编译器会分析每个op的最优内存配额,在运行时每个op的内存配额都是固定的,调度器在检查一个op是否可以被“发射”时,不仅看它的输入数据是否就绪,而且会看它的内存配额是否还有空闲,只有当输入和输出的条件都满足时,这个op才会被发射出去,一旦发射出去,这个op就一定可以一路畅通无阻的执行成功,确保不会被阻塞。 事实证明,这样一个小小的改变,大大简化了系统的复杂度,使得原本棘手的很多问题迎刃而解。感兴趣的朋友可以去 看看OneFlow的actor机制。 图4. 一个op的状态机,只有当输入(in)和输出(out)都就绪的时候才满足触发条件 迄今为止,研发深度学习框架的同行极少有人意识到这一问题,几乎所有框架都是把为一个op的输出数据分配内存的逻辑放在该op的计算逻辑里,且没有分离。 当然也有例外。 最近,华为的MindSpore正在把底层 的图执行器由旧版设计改造成和OneFlow一样的actor机制。 在MindSpore的新版设计里,判断一个op是否可以执行的条件仍只看其输入数据是否就绪,但相对于TensorFlow等框架有一处进步,就是把为一个op的输出数据分配内存的逻辑和这个op的计算逻辑解耦,专门设计了一个负责分配内存的actor来执行内存分配,当这个actor成功分配内存之后再发消息给需要这个内存的op,从而这个op可以畅通无阻的执行下去。 这种设计可能比上文讨论的定制allocator好一点,不过,还是不如OneFlow中的机制简洁。 刚才说到,谷歌的TensorFlow解决这个问题也非常困难,不过他们应该已经意识到这个问题了。他们正在开发新一代的运行时系统TensorFlow Runtime ,目标是在未来完全取代现在TensorFlow臃肿不堪的运行时系统。在新版的设计方案里, 他们对本文讨论的问题具有非常清晰深刻的认识,譬如,在他们的设计文档里有这样一些描述:感兴趣的朋友可以去研究一下新版TensorFlow Runtime的实现,在这一版里,他们的确把“优雅”作为设计目标之一。 然而,本文分析的这一问题,在OneFlow设计之初就已经考虑到了,并且使用了迄今为止我们仍觉得可能是最简单的方法。 注:题图源自pixabyExisting TF kernels encapsulate shape computation and memory allocation within the kernel implementation, making some graph compiler optimizations challenging or infeasible, such as reusing buffers across kernels. In TFRT kernels, shape computation and memory allocation will be hoisted out of the opaque C++ kernel implementations. A core design principle of TFRT is that kernel executions are never allowed to block, as this allows us to have a fine-grained control over the number of compute threads and the thread switching behavior, which is important for achieving high CPU utilization.
点击“阅读原文”,欢迎下载体验OneFlow新一代开源深度学习框架
本文分享自微信公众号 - OneFlow(OneFlowTechnology)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
Recommend
-
47
-
31
信息化的世界,不进则退,慢进亦退。在数据中心、云计算、5G和宽带快速发展的背景下,企业的IT基础设施发生了重大变化。尤其近两年,企业的命运往往与其选择的新思维、新技术紧密相连。有的错过发展时机,导致危机四伏;有的转...
-
6
重新注册 PT 网站后,原有种子“红种” 的简单解决方法 2020-03-27 默认分类
-
4
训练GPT-3,为什么原有的深度学习框架吃不消? - OneFlow深度学习框架的个人空间 - OSCHINA - 中文开源技术交流社区
-
6
警惕人生中的资源诅咒 卫夕指北 • 2021-10-31 14:30:22 来源:卫夕指北 E1329G0...
-
7
数据搬运的“诅咒” | 原有深度学习框架的缺陷② - OneFlow深度学习框架的个人空间 - OSCHINA - 中文开源技术交流社区
-
7
动态调度的“诅咒”| 原有深度学习框架的缺陷③ 为什么要重新设计一个像OneFlow这样的分布...
-
1
保持原有配额,加大品牌建设:2022,金沙酒业打响“摘要价值战” 来源:深蓝财经 浏览:3011 2022-01-17 11:04:27 刚刚过去的2021年,在高端酱酒领域,金沙酒业再一次跑出了让行业惊艳的“...
-
8
V2EX › Windows 升级 Windows 11 后,原有的 windows.old 丢失了... gaohongyuan · 2...
-
4
Excel如何在原有基础上给每行增加一个固定行高 2022-04-01
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK