Infiniband: 8GB/s
为什么快:自己是一套协议,不需要窗口流控,路由更直接;接收方空间够,发送方才发送; 无需CPU介入(by pass CPU,腾出CPU干别的事);
NVLink: 60GB/s!
Infiniband的目标是集群内的高速通信,Ethernet的目标是兼容不同设备的互联
用了拆小包Pipeline;用了GPUDirect直达;忽略CPU和主存,忽略Host buffer、CUDA buffer、网卡buffer;
例子:OpenMPI;
RingAllreduce: (百度,Uber的Horovod)
又叫"Scatter-Reduce + Allgather";
耗时和节点数无关(带宽角度);(延迟角度来看,和节点数有关)
关键在于认识到机器硬件拓扑,然后根据拓扑去构建使得最大的边延迟最小的方案;
NCCL:
1.0支持单机多卡;2.0支持多机多卡;
1,拓扑算法,ring allreduce, double tree拓扑等;2,细节处理好,gpu p2p数据搬运,GPU-Direct支持等;3,节点间支持RDMA和socket,前者在特定网络设备上表现特别好;4,手工实现了一个类似cuda stream的任务队列,方便从host向device派遣任务;5,在cuda kernel上限定每个block使用比较少数thread,集群操作不太影响其他计算任务;6,把host memory映射到device地址空间,从device直接访问host,流式处理数据搬运和计算;7,device之间使用底层通信原语进行同步,而不是cuda api里的event。
byteps:
byteps+NCCL性能高于Horovod+NCCL;
单机内部用的NCCL,机器之间用的自研通信;
解释:allreduce会是数据量的2倍发送+接收(NCCL文档里解释的很清楚,还有好几种通信操作的复杂度);ps是1倍发送+接收;Tensorflow和Mxnet的ps工程实现差劲;
bytesps的server是不存储参数的,只做梯度加和和数据交换;单向传输时间等于(S/N)/(B/N),双向pipeline传输时间略大于这个(加上计算和延时),所以约等于S/B;而RingAllreduce约等于2S/B;根本原因是增加了N台和Worker同等性能的网卡,增加了资源换来的性能提升;
郭传雄的paper :层数越靠前的,梯度传输优先级越高,因为马上就要用更新后的层来计算Forward了;用了分小块传输(充分利用双向带宽,push和pull同时进行),在优先级和网络不闲置方面的最优trade-off。
预取并缓存多少数据,可以让系统视缓存队列空闲程度来自行决定;因为网速快慢变化,consumer消费快慢变化,所以动态调整缓存队列大小和预取强度。
Transform预处理,可以调整并发线程数,多个batch同时处理;多个源文件可以同时open,同时读,同时Transform;
数据cache(首轮慢,后续迭代飞快): 如果Transform之后数据集能在内存放下,就cache住Transform之后数据;如果放不下,就cache住Transform之前的数据(操作系统文件cache会自动做这事儿吧?);如果还是放不下,可以把远程数据cache到本地磁盘,如果Transform之后数据变太大,则可只cache原始数据,如果变不太大,则可cache Transform之后的数据;
batch里的数据Transform如果能用向量或矩阵运算一次性完成,则比一个一个循环来做快很多!
Transform如果先复杂计算,而后增大数据到内存放不下,可以分成2段,在中间进行cache!
ETL:Extract(从本地磁盘或远程存储读取), Transform(数据格式转换), Load(加载至CPU或GPU执行计算)
tf.data.Dataset.range(2)
.interleave( # Parallelize data reading
dataset_generator_fun,
num_parallel_calls=tf.data.experimental.AUTOTUNE
)
.batch( # Vectorize your mapped function
_batch_map_num_items,
drop_remainder=True)
.map( # Parallelize map transformation
time_consumming_map,
num_parallel_calls=tf.data.experimental.AUTOTUNE
)
.cache() # Cache data
.map( # Reduce memory usage
memory_consumming_map,
num_parallel_calls=tf.data.experimental.AUTOTUNE
)
.prefetch( # Overlap producer and consumer works
tf.data.experimental.AUTOTUNE
)