Oneflow概要

原文

  • 厚Compile阶段;薄Runtime阶段;
  • 去中心化调度:以Op(叫做Actor)为核心,每个Actor有输入buffer和输出buffer;输入buffer有可用的且输出buffer有空闲的,来驱动Actor执行
  • 天然支持pipeline并行:各个Actor并行执行;(同一台机器上,Actor之间的数据buffer,通过指针传递)

        “整个数据流的执行像一个网络,数据在网络中的流动就完成了计算,如何避免生产者生产太快,消费者消费不及,以及如何避免生产者生产太慢,消费者感到饥饿的问题,这涉及到对计算、内存、传输带宽的规划,尽可能使系统的瓶颈之处最宽,需要解决流控(flow control)的问题以及资源分配问题(如每个Actor的Register到底分配几个内存块配额),这是非常关键的问题,也是OneFlow系统已解决的。”

  • 数据搬运作为一等公民:一切皆Actor(运算是Actor, 数据搬运也是Actor), Compile期全局规划;
  • 尽可能并行:

      “考虑到分布式训练模型梯度同步时,显存到内存的传输带宽高于机器之间的网络传输带宽,OneFlow会做两级的scatter和gather操作(本机的和各个机器之间的),用于增加locality,提高整体性能;又比如在异步启动深度学习训练时,python端用户的控制逻辑跟OneFlow运行时的执行图是并行执行的,同时OneFlow有一套互斥临界区的设计保证执行的高效性和正确性;数据加载部分无论是从磁盘读数据还是从python端喂数据,OneFlow都能保证尽可能并行,使得计算设备不会因为要等数据而导致性能下降。已有框架如果想要尽可能重叠数据搬运和计算,一般借助多层回调(callback)函数,当嵌套层次过多时,会遇到所谓的callback hell麻烦,正确性和可读性都可能下降。但在OneFlow中,以上的这些并行并发特性,都是在这一套简洁的Actor机制下实现的,解决了令人头秃的callback hell问题。此外,在多机的网络通信部分,OneFlow底层的网络通信库原生支持RDMA的高性能通信,也有一套基于epoll的高效通信设计。而目前最流行的Pytorch,多机还需要通过RPC来做数据同步。”

  • Placement+SBP(Split, Broadcast, PartialSum): 其实就是每个Actor的输入和输出,对应一个逻辑View和一个物理View;这个逻辑View和这个物理View的关系,可以是Split关系(Gather<-->Scatter关系), Broadcast关系,PartialSum关系(Reduce关系);每个Actor可以预先定义几组映射关系,Compile阶段通过剪枝搜索来计算每组关系映射到物理View上后的总开销,取近似最优解;

 

原文

“由于分布式环境下每个机器上是一个进程,所以每个TaskNode都会设置Machine id和Thread id。线程id分配的方式:CPU上是平均分配各个thread id;GPU上,同一个GPU的所有计算Task在同一个计算线程中;所有集合通信的Task在同一个NCCL线程中。这样分配线程id的方式是因为经过实验验证,计算Task在相同线程中速度最快(最小切换开销)。”

 

GPU底层优化

目标:打满计算资源和显存带宽资源

1. 减少Global memory访问

    1.1 Element-wise操作的多个op融合,多个kernel变成1个kernel,中间数据都放在寄存器里,减少了读写Global memory次数;

    1.2 借助Shared memory (例子没看懂)

    1.3 减少访存的大小:把Relu前向结果缓存在bit数组里(而不是bool byte数组),供反向传播时使用;

2. 确保全局内存访问合并

    全局内存访问挨着,就可以减少访存transaction数量;

    尽量对齐,否则可能导致额外多1次transaction;

    例子:Relu写bit数组:使用warp级同步原语(__ballot_sync),warp里每个thread会往同一个int32里写入bit;进一步优化:写完多个int32再多个thread一次写入Global Memory;

3. 减少Kernel计算量

    3.1 有些op(例如transpose这样纯搬运数据的),可以将对byte的操作合成对int64的操作,从而减少指令执行数量;

    3.2 减少除法:有的op的input tensor某些维度是1时,可以删去除法;

    3.3 int32做下标,比int64做下标,计算快;(当Tensor的维度还没大到超过int32时,可以这么做)

    3.4 除法和取余,用除法和乘法减法代替;

4. 掩盖延迟

    4.1 指令延迟(主要取决于读register的延迟);访存延迟;解决:增大并发warp数,即增大occupancy;(Little's law)

    4.2 int8-->double128,减少访存次数,达到访存延迟被隐藏的效果;

5. 其他技巧

    5.1 使用常量做数组下标(使用变量做下标,数组会被放到很慢的local memory;使用常量做下标,数组如果足够小,会被放到registers里)(问答链接

    5.2 一个数据被读了2次,中间如果有写其他数据的指令,则编译器目前无法判断会不会第2次读的是不是第1次的结果,因此会傻瓜读2次;

    5.3 减少分支;尤其是分支里有访存操作的;

 

 

介绍GPU thread hide letency的好文章:

https://www2.eecs.berkeley.edu/Pubs/TechRpts/2016/EECS-2016-143.pdf

同1个warp,当前后指令没有dependency时,可以连续发射多个指令,不管之前发射的指令执行完了没有;

1个SM上有可同时执行多个warp的cores,1个cycle里可以发射1~2个指令;所以,1个SM的throughtput=N指令/cycle;

因为所有threads就在片上的registers里,所以压根就没有context切换这个过程,也就切换得超快!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值