1.引言
為什么要并行
近幾年,依賴大規(guī)模標(biāo)注數(shù)據(jù)和大量的可學(xué)習(xí)參數(shù)掠拳,深度神經(jīng)網(wǎng)絡(luò)才能異軍突起,占得機(jī)器學(xué)習(xí)半壁江山纸肉。然而溺欧,也是因為這兩點使得深度學(xué)習(xí)的訓(xùn)練變得極其困難,尤其是對語音和圖像這樣的大型非結(jié)構(gòu)化數(shù)據(jù)來說柏肪,訓(xùn)練更加困難胧奔。即使通過GPU卡加速可以緩解這個問題,但是單卡上能夠放置的數(shù)據(jù)量還是有限的预吆,所以給超大數(shù)據(jù)集(如圖像數(shù)據(jù)集ImageNet)帶來的加速仍然杯水車薪龙填。基于以上單卡硬件能力的局限性拐叉,進(jìn)一步加速訓(xùn)練就需要訴諸于分布式訓(xùn)練岩遗,即使用單機(jī)多卡或多機(jī)多卡的進(jìn)行模型訓(xùn)練,從而起到加速訓(xùn)練的效果凤瘦。
2.基礎(chǔ)知識
2.1 為什么要了解GPU底層通信知識
目前主流的深度學(xué)習(xí)框架都已經(jīng)封裝了使用上很友好的基于多GPU的分布式數(shù)據(jù)并行訓(xùn)練功能(Distributed DataParrallel, DDP)宿礁,但是只有充分了解了DDP的底層知識和工作原理,才能更好地理解并使用DDP蔬芥,并且排查可能出現(xiàn)的并行加速性能問題梆靖。本文便是嘗試進(jìn)一步深挖多GPU卡的底層工作原理,網(wǎng)上關(guān)于DDP的實現(xiàn)和原理已經(jīng)有很多比較全面的文章(例如Pytorch的官方文檔)笔诵,所以本文不會包含這些內(nèi)容返吻。
2.2 多GPU與CPU的物理連接及拓?fù)?/h3>
不管單機(jī)多卡還是多機(jī)多卡,多個卡之間都會進(jìn)行數(shù)據(jù)交換(推理激活值或者梯度)乎婿。為了能夠更好地了解多GPU卡是如何通信或進(jìn)行數(shù)據(jù)交換测僵,我們首先得從多GPU卡在物理主板上的連接拓?fù)溟_始探究。后面討論的內(nèi)容基礎(chǔ)都是基于Intel X86架構(gòu)谢翎,其他架構(gòu)(如IBM PowerPC)不在討論范圍內(nèi)捍靠。在Intel X86架構(gòu)下討論多GPU卡的物理連接,則不得不討論PCI和PCIe相關(guān)內(nèi)容森逮。
2.2.1 PCI
我們知道榨婆,現(xiàn)代計算機(jī)是以存儲(寄存器和內(nèi)存)和CPU為中心的(主要是存儲),其他的如鍵盤褒侧、顯示器和磁盤等設(shè)備相對內(nèi)存和CPU來說都是外圍設(shè)備(又稱I/O設(shè)備良风,簡稱外設(shè))颜武,因為他們本身沒有匯總多方數(shù)據(jù)并進(jìn)行邏輯運算的能力。例如拖吼,顯示器顯示的字符或圖像只是CPU計算的結(jié)果鳞上,鍵盤也只是告知CPU人類輸入了什么字符,收到字符后的其他操作就是CPU的任務(wù)了吊档。有些外設(shè)是用來與人類進(jìn)行交互的篙议,例如顯示器和鍵盤,它們一個從CPU獲取數(shù)據(jù)怠硼,一個向CPU發(fā)送數(shù)據(jù)鬼贱;有些外設(shè)是提供數(shù)據(jù)持久化的,如磁盤香璃,因為寄存器和內(nèi)存都是非永久性存儲这难,斷電后所有數(shù)據(jù)就會丟失,使用磁盤我們可以將CPU執(zhí)行的結(jié)果進(jìn)行持久化保存葡秒;有些外設(shè)是進(jìn)行多個主機(jī)CPU之間數(shù)據(jù)交換的姻乓,例如網(wǎng)卡;而有些外設(shè)執(zhí)行的任務(wù)更加特殊眯牧,比如GPU蹋岩,它的作用主要是幫助CPU分擔(dān)大量的數(shù)值計算任務(wù),一開始GPU的作用主要是執(zhí)行圖形相關(guān)運算的学少,從而讓CPU騰出更多的時間執(zhí)行邏輯計算剪个。從上面的描述我們可以看出,大量的外設(shè)都要與CPU或內(nèi)存進(jìn)行數(shù)據(jù)交換版确,外設(shè)有很多扣囊,CPU只有一個或幾個,這時CPU就成為了稀缺資源绒疗,因為同一時間單一CPU不可能同時與磁盤和顯示器進(jìn)行通信侵歇。這時就需要有一套提前聲明的規(guī)定,也就是協(xié)議忌堂,來規(guī)范所有外設(shè)與CPU的通信行為盒至,在Intel X86架構(gòu)中,這便是早期的PCI協(xié)議士修。
PCI(Peripheral Component Interconnect,外圍設(shè)備互連)總線和設(shè)備樹是X86硬件體系架構(gòu)中很重要的組成部分樱衷,幾乎所有的外圍硬件都以這樣或那樣的形式連接到PCI設(shè)備樹上棋嘲。下面是桌面系統(tǒng)主機(jī)常見的PCI總線樹。
圖中有兩個總線矩桂,分別為“PCI Local Bus #0”和“PCI Local Bus #1”沸移。從圖中我們可以了解之所以將PCI總線稱為總線樹,是因為PCI支持總線擴(kuò)展,即當(dāng)0號總線不夠用時(接入更多的設(shè)備雹锣,但總線插槽數(shù)有限)网沾,可以通過“PCI-to-PCI Bridge”進(jìn)行擴(kuò)展,PCI-to-PCI Bridge可以連接其他總線蕊爵,如果以與CPU直接連接的總線作為樹的根節(jié)點的話辉哥,其他的外設(shè)加上通過擴(kuò)展增加的外設(shè)就構(gòu)成了一個設(shè)備樹。
另外攒射,從圖中也可以看出PCI總線結(jié)構(gòu)是一種共享總線結(jié)構(gòu)醋旦,即掛載在同一總線上的設(shè)備之間是共享總線帶寬的。隨著PCI設(shè)備讀寫速度不斷提高(如顯卡会放、網(wǎng)卡等設(shè)備)饲齐,這種架構(gòu)就變得不適用了,經(jīng)過幾代的更新咧最,最終從PCI架構(gòu)演變出目前主流的PCIe架構(gòu)捂人。
2.2.2 PCIe
PCIe(Peripheral Component Interconnect Express),即高速的PCI矢沿,其在PCI的基礎(chǔ)上做了很多擴(kuò)展先慷,其中一個就是“PCIe是點對點結(jié)構(gòu),而PCI是總線結(jié)構(gòu)”咨察,一個典型的PCIe系統(tǒng)結(jié)構(gòu)圖如下:
點對點意味著每一個PCIe設(shè)備都擁有自己獨立的數(shù)據(jù)連接论熙,設(shè)備之間數(shù)據(jù)傳輸并發(fā)互不影響,而對于PCI共享總線方式摄狱,PCI總線上只能有一個設(shè)備進(jìn)行通信脓诡,一旦PCI總線上掛接的設(shè)備增多,每個設(shè)備的實際傳輸速率就會下降媒役,性能得不到保證祝谚。PCIe以點對點的方式處理通信,每個設(shè)備在要求傳輸數(shù)據(jù)的時候各自建立自己的傳輸通道酣衷,對于其他設(shè)備這個通道是封閉的交惯,這樣的操作保證了通道的專有性,避免其他設(shè)備的干擾穿仪。例如席爽,圖中最右下角的兩個外設(shè)(兩個PCI Express Endpoint)想要通信的話,只需要通過直連的Switch設(shè)備進(jìn)行數(shù)據(jù)交換(類似于網(wǎng)絡(luò)交換機(jī))啊片,而不必通過“外設(shè)1-->CPU-->外設(shè)2-->CPU-->外設(shè)1”這樣的鏈路進(jìn)行數(shù)據(jù)交互只锻,這樣就不會左上角的外設(shè)與其他設(shè)備或者CPU進(jìn)行數(shù)據(jù)交互的帶寬。當(dāng)然紫谷,這兩個外設(shè)的數(shù)據(jù)交互還是會影響掛載在同一Switch設(shè)備上的其他外的帶寬齐饮,但這樣也大大降低了PCI中外設(shè)數(shù)據(jù)必須通過CPU才能進(jìn)行交互的問題捐寥。
圖中的Root Complex是各種資源的集合,如中斷控制器祖驱、電源管理控制器握恳、內(nèi)存控制器、錯誤檢測和報告邏輯等捺僻。Root Complex也包含一個內(nèi)部總線(Host Bridge)乡洼,它表示整個PCIe樹結(jié)構(gòu)中的0號總線。它代表處理器發(fā)起外設(shè)事務(wù)請求陵像,從它的端口發(fā)送數(shù)據(jù)包或在它的端口接收數(shù)據(jù)包就珠,然后傳輸?shù)絻?nèi)存。我們在下文中還會遇到Root Complex和Host Bridge醒颖。
2.2.3 NUMA
服務(wù)器的情況要復(fù)雜一點妻怎,這需要了解NUMA的概念:就如之前說的,在若干年前泞歉,對于x86架構(gòu)的計算機(jī)逼侦,那時的內(nèi)存控制器還沒有整合進(jìn)CPU,所有內(nèi)存的訪問都需要通過北橋芯片來完成腰耙。此時的內(nèi)存訪問如下圖所示榛丢,被稱為UMA(uniform memory access, 一致性內(nèi)存訪問 )。這樣的訪問對于軟件層面來說非常容易實現(xiàn):總線模型保證了所有的內(nèi)存訪問是一致的挺庞,不必考慮由不同內(nèi)存地址之前的差異晰赞。
之后的x86平臺經(jīng)歷了一場從“拼頻率”到“拼核心數(shù)”的轉(zhuǎn)變(核心就是CPU中最小的邏輯運算單元,核心越多选侨,同時并發(fā)執(zhí)行任務(wù)的任務(wù)數(shù)量就越多)掖鱼,越來越多的核心被盡可能地塞進(jìn)了同一塊芯片上,各個核心對于內(nèi)存帶寬的爭搶訪問成為了瓶頸,因此X86推出了NUMA(Non-uniform memory access, 非一致性內(nèi)存訪問)架構(gòu)(還有其他的原因),示意圖如下:
從圖中我們可以得出NUMA架構(gòu)的兩個特點:
1.CPU芯片被劃分成不同的組汹桦,稱為node,每個node有自己獨立的內(nèi)存訪問地址技健,每個node也就有自己的內(nèi)存單元;
2.如果想要跨node訪問內(nèi)存(如node0想要訪問下面的內(nèi)存),則需要通過跨node的CPU通信進(jìn)行數(shù)據(jù)交互(node之間通過QPI連接)。
下圖是我手頭使用的一臺服務(wù)器的node和CPU數(shù)量妥凳,該信息是命令lscpu
的顯示結(jié)果, 從圖中可以看出码泛,這條服務(wù)器將2個CPU芯片(每個芯片有20個核心)劃分成兩個組(node), 其中0 ~ 9和20 ~ 29號核心屬于屬于node0猾封,10 ~ 19和30~39核心屬于node1:
既然內(nèi)存劃分給不同的CPU組(即node),那么類似地噪珊,將PCI設(shè)備劃分給不同的node晌缘,也會提高外設(shè)與特定CPU的數(shù)據(jù)交換效率。同樣痢站,如果想要跨node訪問PCI外設(shè)磷箕,也需要經(jīng)過QPI連接進(jìn)行跨node的CPU通信進(jìn)行數(shù)據(jù)交互。在NUMA架構(gòu)下具有兩個node(即兩個CPU芯片組)的PCIe物理結(jié)構(gòu)如下圖阵难,圖中每個node只有一個CPU芯片岳枷,所以用CPU代替了node:
上圖中差不多就是簡易的現(xiàn)代服務(wù)器的物理連接結(jié)構(gòu)圖,從圖中我們可以看出呜叫,如果一個服務(wù)器上掛載了多個GPU卡空繁,可能由于每個卡掛載的物理位置不同,GPU之間的通信鏈路方式就會有多種朱庆,使用命令nvidia-smi topo --matrix
可以直接獲得服務(wù)器上每兩個卡之間的物理通信方式:
可以看出盛泡,英偉達(dá)給出了6中GPU卡的物理通信方式,下面我們可以結(jié)果上面的PCIe物理結(jié)構(gòu)圖來了解這些通信方式:
SYS: 通過QPI(PCIe + QPI總線)跨NUMA node間GPU通信娱颊,相當(dāng)于上上圖中的GPU1到GPU5傲诵;
NODE: 單個NUMA node內(nèi)經(jīng)過Host Bridge PCIe總線通信(一個NUMA node上有多個CPU芯片),沒遇到過這種情況,就不舉例子了箱硕;
PHB: 經(jīng)過Host Bridge(Root complex中)的PCIe總線通信拴竹,同一Root complex下多個PCIe總線,相當(dāng)于上上圖中的GPU1到GPU3剧罩;
PXB: 跨越多個PCIe Bridge (switch)栓拜,但沒有經(jīng)過Host Bridge PCIe總線,相當(dāng)于上上圖中的GPU3到GPU4惠昔;
PIX: 經(jīng)過一個PCIe Bridge (switch)通信幕与,即掛載在同一個PCIe Bridge上的GPU卡之間通信,相當(dāng)于上上圖中的GPU1到GPU2舰罚;
NV#: 使用NVLink通信(后面會介紹)纽门;
描述中所指的Host Bridge就是Host主橋,是連接處理器和PCI總線的一個設(shè)備器件营罢,用來隔離處理器的存儲器域與PCI總線域的特殊橋片赏陵,管理PCI總線域,集成在Root Complex中饲漾,此處可以認(rèn)為就是圖中的Root Complex蝙搔。
從上面的例子可以直觀地看出,兩個GPU卡的物理距離越近(如GPU1到GPU2)考传,則通信效率越高吃型,距離越遠(yuǎn)效率越低,此處的效率一般體現(xiàn)在通信時延上僚楞。
2.3 GPU通信技術(shù)
GPU強(qiáng)大的并行計算能力勤晚,大大提升了運算性能枉层。隨著運算數(shù)據(jù)量的不斷攀升,GPU間需要交換大量的數(shù)據(jù)赐写,GPU通信性能成為并行計算非常重要的性能指標(biāo)鸟蜡。前面介紹的NUMA架構(gòu)和PCIe協(xié)議只是物理層面的協(xié)議(類似于網(wǎng)絡(luò)協(xié)議中的物理層和數(shù)據(jù)鏈路層),如何在其上進(jìn)行高效的GPU通信是接下來的內(nèi)容(類似于網(wǎng)絡(luò)協(xié)議中的IP層和傳輸層)挺邀。
GPU通信的發(fā)展簡史如下:
GPUDirect Shared Memory (2012) : Nvidia在PCIe上實現(xiàn)了單機(jī)上的GPUDirect Shared Memory 技術(shù)揉忘;
GPUDirect P2P (2014): Nvidia在PCIe上實現(xiàn)了單機(jī)上的GPUDirect P2P技術(shù);
NVLink(2014) :解決了單機(jī)多卡通信時PCIe瓶頸問題端铛;
GPUDirect RDMA(2014):提升多機(jī)多卡通信性能泣矛;
2.3.1 GPUDirect Shared Memory
Nvidia在PCIe上實現(xiàn)了單機(jī)上的GPUDirect Shared Memory 技術(shù),使得GPU與第三方PCIe設(shè)備通過共享的固定的(pinned)CPU內(nèi)存實現(xiàn)共享內(nèi)存訪問從而加速通信禾蚕,原理如下圖所示:
此處的“固定的CPU內(nèi)存”指的就是我們在使用Pytorch的DataLoader時指定的pin_memory參數(shù)您朽,由于設(shè)置pin_memory=True
后會導(dǎo)致部分CPU內(nèi)存無法被其他進(jìn)程使用并且無法參與Swap換出到磁盤,如果在主機(jī)內(nèi)存不足時設(shè)置該參數(shù)夕膀,則會造成服務(wù)器運行效率降低虚倒。
2.3.2 GPUDirect P2P
后來GPUDirect又增加了相同PCI Express root complex下的GPU之間的內(nèi)存數(shù)據(jù)直接訪問或交換的能力,即Peer to Peer(P2P) Direct Access和Direct Transfers产舞,進(jìn)一步降低了GPU數(shù)據(jù)交換的成本魂奥,原理如下圖所示:
2.3.3 NVLink
由于PCIe帶寬瓶頸問題,Nvidia直接放棄使用PCIe易猫,提出了NVLink通信協(xié)議耻煤,能在多GPU之間和GPU與CPU之間實現(xiàn)更高的通信帶寬,但是放棄PCIe也就相當(dāng)于放棄Intel X86准颓,所以目前在主流的X86服務(wù)器上是看不到NVLink的哈蝇。
2.3.4 GPUDirect RDMA
對于大規(guī)模深度學(xué)習(xí)訓(xùn)練任務(wù),單機(jī)已經(jīng)無法滿足計算要求攘已,多機(jī)多卡的分布式訓(xùn)練成了必要的需求炮赦,多機(jī)間的通信成為了分布式訓(xùn)練性能的重要指標(biāo),因此GPUDirect增加了對RDMA( Remote Direct Memory Access )的支持样勃,使得GPU可以不經(jīng)過CPU直接訪問其他主機(jī)上GPU卡的內(nèi)存數(shù)據(jù)吠勘。
目前我們使用的X86單個服務(wù)器用不到NVLink和RDMA,主要GPU通信受益于P2P技術(shù)峡眶,下面是我手頭使用的服務(wù)器在使用P2P后的GPU卡通信時延對比(該數(shù)據(jù)使用p2pBandwidthLatencyTest工具得到)剧防。
下圖是0~7卡P2P連接情況,1代表支持P2P連接辫樱,0代表不支持:
從圖中紅框可以看到峭拘,使用P2P技術(shù)后GPU間通信實驗降低了2~3倍。
2.4 多卡信息收集(collective)通信技術(shù)NCCL
之前介紹的都是GPU的物理連接和通信協(xié)議,開發(fā)者想要使用這些功能進(jìn)行多卡通信(如深度學(xué)習(xí)框架中支持多卡方式訓(xùn)練模型)鸡挠,則需要在其之上封裝一個友好的庫(類似于操作系統(tǒng)提供的socket通信編程接口)辉饱。這便是NCCL(Nvidia Collective multi-GPU Communication Library)誕生的目的:它是一個實現(xiàn)多GPU的數(shù)據(jù)匯集(collective)通信庫,支持的數(shù)據(jù)通信原語有reduce宵凌,all-reduce鞋囊,broadcast 止后,gather瞎惫,all-gather等;使用ring-based的collective通信方式译株,可以顯著降低通信時間瓜喇;同時NCCL做了很多優(yōu)化,可以在PCIe歉糜、NVLink乘寒、InfiniBand上實現(xiàn)較高的通信速度。
下面簡單介紹幾個常用NCCL通信原語的原理:
- NCCL通信原語——reduce匪补,從多個sender那里接收數(shù)據(jù)伞辛,最終combine到一個節(jié)點上
- NCCL通信原語——all-reduce,從多個sender那里接收數(shù)據(jù)夯缺,最終combine到每一個節(jié)點上
- NCCL通信原語——broadcast蚤氏,從單個sender數(shù)據(jù)發(fā)送到其他節(jié)點上
- NCCL通信原語——gather,將多個sender上的數(shù)據(jù)收集到單個節(jié)點上
另外踊兜,需要額外介紹下NCCL的ring-base collective通信方式竿滨,它將所有的通信節(jié)點(GPU卡)通過首尾連接形成一個單向環(huán),數(shù)據(jù)在環(huán)上依次傳輸捏境,使用ring-base collective具有以下好處:
- 去掉數(shù)據(jù)(參數(shù))服務(wù)器節(jié)點的概念于游,所有在環(huán)中的節(jié)點功能相同,通過分?jǐn)偼ㄐ咆?fù)載從而降低單個節(jié)點的通信壓力垫言;
- 可以實現(xiàn)通信時間不隨節(jié)點數(shù)的增加而增加贰剥,只和數(shù)據(jù)總量以及帶寬有關(guān);
下面是傳統(tǒng)的單server多worker和多server多worker通信模式筷频,不管哪種通信模式蚌成,它們的最大缺點是隨著通信節(jié)點的增加,將極大增加網(wǎng)絡(luò)通信壓力截驮,尤其是server端的通信壓力笑陈,因為所有節(jié)點的數(shù)據(jù)都要先匯聚到server上,然后在分發(fā)到每個節(jié)點上葵袭。
下面是以傳統(tǒng)方式broadcast通信原語實現(xiàn)的示意圖:
從上面的圖中可以看出傳統(tǒng)broadcast操作的通信時間適合GPU卡的數(shù)量相關(guān)的(圖中公式)涵妥,下面用ring-base collective通信方式實現(xiàn)broadcast通信原語的示意圖,可以看出當(dāng)數(shù)據(jù)分塊的數(shù)量
遠(yuǎn)大于GPU卡數(shù)
時坡锡,通信耗時接近
蓬网,而該值只由通信數(shù)據(jù)的總量
和通信帶寬
有關(guān)窒所,與GPU卡數(shù)無關(guān)。
all-reduce操作也有類似的操作:
下圖是NCCL ring-base collective通信方式在單機(jī)8卡之間構(gòu)造的通信環(huán)路(該圖是理想情況下:每四張卡掛載同一Switch上):
目前主流的深度學(xué)習(xí)框架帆锋,使用NVIDIA GPU計算時吵取,DDP的多卡數(shù)據(jù)同步基本上使用NCCL是效率最高的,如下是Pytorch使用DDP時指定NCCL作為通信方式的官方示例代碼:
import torch
import torch.multiprocessing as mp
def example(rank, world_size):
# create default process group
torch.distributed.init_process_group(backend="nccl",
init_method="tcp://127.0.0.1:8790",
world_size=world_size, rank=rank)
# create local model
model = torch.nn.Linear(10, 10).to(rank)
# construct DDP model
ddp_model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[rank])
# define loss function and optimizer
loss_fn = torch.nn.MSELoss()
optimizer = torch.optim.SGD(ddp_model.parameters(), lr=0.001)
# forward pass
outputs = ddp_model(torch.randn(20, 10).to(rank))
labels = torch.randn(20, 10).to(rank)
# backward pass
loss_fn(outputs, labels).backward()
# update parameters
optimizer.step()
def main():
world_size = 2
mp.spawn(example, args=(world_size,), nprocs=world_size, join=True)
if __name__=="__main__":
main()
以上就是本文的所有內(nèi)容锯厢,如有錯漏之處歡迎指正皮官。
3.參考資料
- 深挖NUMA
- 淺析GPU通信技術(shù)(上)-GPUDirect P2P
- https://developer.aliyun.com/article/599183
- https://developer.aliyun.com/article/603617
- 如何理解Nvidia英偉達(dá)的Multi-GPU多卡通信框架NCCL?
- 深入PCI與PCIe之一:硬件篇
- THE PCI EXPRESS BUS
- PCI Overview: PCI vs PCI Express
- Horovod: fast and easy distributed deep learning in TensorFlow
- NCCL: ACCELERATED MULTI-GPU
COLLECTIVE COMMUNICATIONS