一、Nccl AllReduce基本原理:
allreduce是collective communication中的一種,其他種類的還有:Broadcast、Scatter、Gather、Reduce等
具體含義可以參考文檔:https://images.nvidia.com/events/sc15/pdfs/NCCL-Woolley.pdf、
其中nccl采用一種Undirectional-Ring的單向環算法,可以實現同步時間與卡的個數無關,以BroadCast為例:
假設有4塊GPU,傳輸的數據量為N,傳輸帶寬為B(單機多卡間的傳輸帶寬可以通過cuda/sample下的p2pBandwidthLatencyTest得到),如果按照順序發送的方式,完成整個BroadCast的時間(同步時間)為:(4-1)N/B,即傳輸時間與卡的個數成正比.
如果將N大小的數據分成S份,然后GPU0每次只傳輸一份數據給GPU1,依次類推,那么總時間是GPU0傳輸到GPU1的時間 + 最后一份數據從GPU1到GPU3的時間,即:SN/(SB) + (4-2)*N/(SB)=(S+4-2)N/SB = N/B,可以看出若S無窮大,這樣同步時間就與卡的個數無關了.
理解了BroadCast的例子再去理解AllReduce的例子就好多了,Allreduce分為兩個階段:Reduce-Scatter + AllGather,具體可參考:http://andrew.gibiansky.com/
首先經過Reduce-Scatter在每個GPU上各算出一部分和,然后在通過AllGather傳遞給全部GPU.假設傳輸的數據大小為K,GPU個數為N,
通過文章中的代價分析可以得出:每個GPU傳輸的數據量大小為K/N,Reduce-Scatter傳輸的次數為N-1次,AllGather同樣為N-1次,因此每個GPU傳輸的數據總量均為:2(N-1)K/N.(請記住這個值)
這里注意,文章https://images.nvidia.com/events/sc15/pdfs/NCCL-Woolley.pdf中的AllReduce實現是不同的,如圖所示:
文中的意思很明顯是分段執行Reduce-Scatter和AllGather,但是其實數據傳輸量也是2(N-1)K/N,感興趣的可以自己算一下.
二、BytePS是如何減少數據傳輸量的.
以下內容主要參考:https://www.zhihu.com/question/331936923/answer/732262268
首先雖然頭條將其命名為PS(Params Server),但是我的理解是:其借鑒了PS的方式實現的卻是AllReduce.
以上圖為例,假設有4個worker節點和一個PS節點,此時PS不再持有任何params,只是每次接受梯度做aggregation,一般都是mean(這個階段相當於reduce-scatter),然后再傳回每個worker(這個階段相當於AllGather),本質就是實現了AllReduce,這時PS的作用是為了增加帶寬.那這種實現方式相比Ring-AllReduce傳輸量有多少呢?
假設worker數為N,每個worker傳輸數據大小為M,並且一次性全部傳到PS端,那么PS一次接受的數據量為NM(假設同時算完),然后做mean操作,算完了再傳回就又需要NM的傳輸量,不做任何優化傳輸數據總量為2NM(浪費了雙向帶寬). 如何優化?
這里首先借鑒Nccl的BroadCast思路,將M大的數據分為K份,每次只傳輸M/K大小的數據,然后在此M/K大小的數據做完取mean操作之后馬上傳回全部worker,而同時下一份M/K大小的數據傳入PS,這樣就相當於①傳數據+計算(reduce-scatter)②傳回worker(allGather)形成一個Pipeline,並且利用了雙向帶寬,這樣就等價於帶寬變為2倍,傳輸的數據量減半,即變為NM.
第二次優化就是:加機器. 如圖所示:
加機器就等價於帶寬繼續增加,這樣每個PS只需要處理M/2的數據量就可以了.假設PS的數量等於worker的數量,那么數據傳輸量理想情況就是M,此值是小於2(N-1)*M/N的,理論上正好是Ring-AllReduce數據傳輸量的一半,如果能保證機器的傳輸帶寬都是B,那么同步時間就是M/B. 如果PS節點更多,那么數據傳輸量將更少,同步時間也將更少.