雙引擎 GPU 容器虛擬化,用戶態(tài)和內(nèi)核態(tài)的技術(shù)解析和實踐分享
?如何讓硬件算力發(fā)揮最大效率,是所有資源運營商和用戶非常關(guān)注的問題。百度作為一家領(lǐng)先的 AI 公司,擁有可能是業(yè)界最全的 AI 應(yīng)用場景。
在這篇文章中,將和大家分享和討論 GPU 容器虛擬化在復(fù)雜AI場景中的解決方案和廠內(nèi)的最佳實踐。
下面這張圖片的左右兩部分,在不同場合下已經(jīng)多次展示過,放到這里主要想強調(diào)算力需求 —— 硬件算力的指數(shù)型增長,與真實應(yīng)用場景中利用率偏低資源浪費之間的矛盾。
左邊的部分是 OpenAI 統(tǒng)計的數(shù)據(jù),從 2012 年以來,模型訓(xùn)練所需的算力每 3.4 個月翻一倍,截止到 AlphaGoZero 這類的大模型,訓(xùn)練算力已經(jīng)增長了 30 萬倍,并且這種趨勢還在繼續(xù)。一方面,隨著算力需求的增長,主流 AI 加速單元計算性能也在以每兩年翻一倍的速率增加。另一方面,資源利用效率卻制約著硬件效能的充分發(fā)揮。
右邊的部分是 Facebook 在 2021 年對數(shù)據(jù)中心 Machine Learning 負載分析的結(jié)果。大量的 AI 算力損失在故障、調(diào)度、時間片浪費、空間單元浪費等環(huán)節(jié),真正的算力利用率不到 30%。我們相信,這也是國內(nèi)各大基礎(chǔ)設(shè)施運營商所面臨的現(xiàn)狀。
剛才提到在線集群不到 30% 利用率可能不符合很多同學(xué)的認知。在線的很多同學(xué)可能是模型和算法的開發(fā)者。我們普遍的認知是,在訓(xùn)練和測試過程中利用率可以保持很高的水平,甚至可以達到 100% 利用率。
但模型在生產(chǎn)環(huán)境上線,會受到很多約束,這些約束導(dǎo)致利用率遠遠達不到我們的預(yù)期。?
下面我們用有限的篇幅總結(jié)一下主要的制約因素:
- 模型特點:每個模型網(wǎng)絡(luò)不同,調(diào)用的底層算子組合不同,很大程度上會影響 GPU 的利用率。
- 服務(wù) SLA:不同場景下的服務(wù)需要不同的 SLA,有的服務(wù)實時性要求較高,甚至需要嚴格控制在 10ms 以內(nèi),那么這些服務(wù)就不能通過增加 batchsize 的方式提升利用率,甚至 batchsize 只能為 1。
- 流量模式:不同模型算法服務(wù)于不同的應(yīng)用場景,比如 OCR 識別,可能在工作期間被頻繁調(diào)用。而語音識別則更多的在通勤時間或者娛樂休閑時才會被調(diào)用,這樣就導(dǎo)致了一天中 GPU 利用率的峰谷波動。
- 優(yōu)化效果:根據(jù)模型的迭代頻率以及覆蓋場景的不同,模型的優(yōu)化粒度也不盡相同??上攵?,一個未經(jīng)充分優(yōu)化的模型利用率也很難達到較高的水平。
- 容量冗余:模型上線前都要經(jīng)過詳細的容量規(guī)劃,最大流量是多少,是否需要多地域,在此過程中會預(yù)留難以忽略的容量冗余,這些冗余在平時也造成了算力的浪費。
在上面種種約束條件的制約下,真實生產(chǎn)環(huán)境的利用率可能是接下來我們要展示的。我們從復(fù)雜多變的在線生產(chǎn)環(huán)境中抽象出這幾種利用率模式。
- 均值偏低型:如左上圖,為一個真實的在線推理業(yè)務(wù),由于模型特點和服務(wù) SLA 的限制,GPU 的峰值利用率只有 10%,平均利用率會更低。
- 峰谷波動型:如左下圖,是典型的在線推理業(yè)務(wù)的利用率模式,服務(wù)在白天會達到高峰,在深夜至第二天早上是利用率的低谷,全天平均利用率只有 20% 左右,低谷利用率只有 10% 不到。
- 短時激增型:如右上圖,利用率曲線基本與左下圖一致,但在夜間黃金時段會有兩個明顯的利用率高峰,高峰階段的利用率高達 80%,為了滿足高峰階段的服務(wù)質(zhì)量,該服務(wù)在部署過程中會預(yù)留不小的 buffer,資源平均利用率也剛剛超過 30%。
- 周期觸發(fā)型:如右下圖,是典型在線訓(xùn)練場景的利用模式,在線訓(xùn)練任務(wù)介于離線訓(xùn)練和在線推理之間,這是一種周期性批處理的任務(wù)。例如每 15 分鐘會有一批數(shù)據(jù)到達,但這批數(shù)據(jù)的訓(xùn)練只需要 2-3 分鐘,大量的時間 GPU 處于閑置狀態(tài)。
AI應(yīng)用場景復(fù)雜多變,上面只是列舉了四種典型場景。如何在復(fù)雜場景中,平衡業(yè)務(wù)性能與資源效率,是我們在 GPU 虛擬化中遇到的第一個挑戰(zhàn)。
GPU 虛擬化過程中我們面臨的第二個挑戰(zhàn)就是缺乏完善的 GPU 隔離與混布機制。
我們以目前主流的 NVIDIA GPU 為例。典型的 AI 軟硬件生態(tài)都分為這樣幾個層次 ——應(yīng)用 & 框架層,運行時層,驅(qū)動層,硬件層。
首先最上層是用戶的應(yīng)用,這里包含了各種常見的框架 PaddlePaddle、TensorFlow、PyTorch 等等。在應(yīng)用層之下是硬件提供商封裝的 API 接口層,包含各類常用算子庫與硬件運行時訪問接口。在這層 API 接口之下,是與硬件溝通的驅(qū)動層,該層位于內(nèi)核態(tài),是直接與設(shè)備溝通的軟件接口層。位于最底層是真正的 AI 加速硬件,負責(zé)算子的執(zhí)行。
傳統(tǒng)的虛擬化方案,都會結(jié)合驅(qū)動內(nèi)核態(tài)以及硬件虛擬化邏輯實現(xiàn)。這兩個層次是硬件提供商最核心的 IP,一般是閉源的。后續(xù)會提到,當前 GPU 原生的的隔離機制在靈活性和分配力度上都無法滿足云原生場景下的使用需求。
除了隔離機制,現(xiàn)有的混布機制也很難滿足復(fù)雜場景的需求,我們看到業(yè)界有很多共享調(diào)度的開源方案,這些開源方案只是從資源的層面把兩個任務(wù)簡單的調(diào)度到一張卡上。在實際場景中,簡單的共享會造成業(yè)務(wù)之間相互影響,長尾延遲甚至吞吐的惡化導(dǎo)致簡單共享無法真正應(yīng)用于生產(chǎn)環(huán)境。
在上文利用率模式分析一節(jié)我們看到不同的業(yè)務(wù),不同的場景下,利用率模式都不盡相同。如何抽象業(yè)務(wù)場景,定制混布方案,是生產(chǎn)環(huán)境落地的關(guān)鍵。
為了讓大家更全面地了解 GPU 的發(fā)展以及虛擬化歷史,這里我們用一張圖來展示 GPU 虛擬化發(fā)展史。
GPU 應(yīng)用于通用計算最早可以追溯到 G80 時代的 Tesla 架構(gòu),是第一代實現(xiàn)統(tǒng)一著色器的架構(gòu),用通用處理器 SM 替代了原來的頂點、像素管線分離的圖形圖像處理器。
百度最早引進的 GPU 可以追溯到 Fermi 架構(gòu)。從這個時間點開始,業(yè)界就出現(xiàn)了一批虛擬化方案,其中大部分以 API 劫持為主。這里的典型代表是 rCUDA,該項目最初由學(xué)術(shù)團體維護,直到近期,還保持一定頻率的更新和迭代,但看起來以學(xué)術(shù)研究為主,并沒有在生產(chǎn)環(huán)境大范圍使用。
百度大規(guī)模引入 GPU 是在 Kepler 架構(gòu),Kepler 架構(gòu)開啟了百度自研的超級AI計算機X-MAN 時代。X-MAN 1.0 首次實現(xiàn)單機 16 卡配置,可以在 PCIe 硬件層面實現(xiàn) CPU 和 GPU 的動態(tài)綁定和靈活配比。受限于單卡性能,當時更多的考慮是擴展,而不是切分。
隨后的 Pascal 架構(gòu)、Volta 架構(gòu)、Turing 架構(gòu)性能有了飛速提升,這時虛擬化的需求日益顯著起來。我們看到,最早從 Kepler 架構(gòu),NV 官方提供了 GRID vGPU 虛擬化方案,最開始主要是面向圖形渲染和遠程桌面場景。在 2019 年前后,針對 AI 和高性能計算場景也提供了解決方案。但這些方案都是基于虛機的,在 AI 場景中很少使用。
在 Ampere 這一代,NV 推出了 MIG 實例切分方案,該方案在硬件層面實現(xiàn)了 SM、MEM、L2 Cache 等多種硬件資源的切分,提供了良好的硬件隔離性能。但該方案從Ampere 架構(gòu)開始支持,且對于卡的型號還有一定的限制。只有 A100、A30 少數(shù)幾個型號可以支持。而且即使切分之后,單個實例的性能也超過了 T4 算力,并無法很好地解決當前生產(chǎn)環(huán)境的效率問題。
大家對 GPU 架構(gòu)與虛擬化歷史脈絡(luò)有了一些印象之后,我們來詳細介紹下實現(xiàn) GPU虛擬化的幾個主要層次,或者說是技術(shù)路線。
實現(xiàn)資源虛擬化隔離,首先需要資源在時間或空間維度是可分的,在用戶視角看來就是多個任務(wù)可以并發(fā)(concurrent)或并行(parallel)地執(zhí)行。
這里我們在用戶態(tài)、內(nèi)核態(tài)、硬件多個層次上討論一下并行或并發(fā)空間。
由于 NV 的軟硬件生態(tài)是閉源的,這里的示意圖是我們綜合架構(gòu)白皮書,逆向論文和我們自己的理解而繪制的,不準確的地方還希望大家及時指正。
用戶態(tài)方案
我們從上至下來看這張圖,首先多個進程在 GPU 來看,天然就是并發(fā)的,也就是時分復(fù)用的。驅(qū)動和硬件負責(zé)以時間片輪轉(zhuǎn)的方式進行任務(wù)的切換。利用這層機制,我們可以在 API 層面上實現(xiàn)計算資源、顯存資源的限制,達到虛擬化的效果。這里的 API 可以分為兩層,一層是驅(qū)動 API,這層 API 緊貼驅(qū)動,是所有上層調(diào)用訪問 GPU 必經(jīng)之路,只要控制了這層 API,就相當于控制了用戶的資源訪問。這里先提一句, NV 提供的 MPS 技術(shù)可以實現(xiàn)空分復(fù)用,這也為業(yè)務(wù)性能的進一步優(yōu)化提供了可能。在后續(xù)落地實踐部分我們會詳細展開。
內(nèi)核態(tài)方案
再往下一層是內(nèi)核態(tài),無論是虛機層面的全虛擬化、半虛擬化,還是近兩年各大云廠商的容器方案,都是在內(nèi)核層實現(xiàn)了系統(tǒng)調(diào)用攔截和 MMIO 劫持,內(nèi)核態(tài)最大的困難在于很多寄存器和 MMIO 行為沒有很好的文檔說明,這些都需要復(fù)雜的逆向工程。
硬件方案
內(nèi)核態(tài)之下是硬件層,真正的并行是在這一層進行保證的,無論是 NV 的 MIG 技術(shù)還是百度昆侖的 SR-IOV 技術(shù),都在硬件邏輯上進行了算力切分,實現(xiàn)了真正的并行和空分復(fù)用。如昆侖可以實現(xiàn) 1/3,1/2 的硬件劃分,A100 可以實現(xiàn)最小 1/7 粒度的資源劃分。
上面我們花了不小的篇幅向大家介紹了 GPU 虛擬化的挑戰(zhàn)和現(xiàn)狀,接下來我們看百度內(nèi)部是如何應(yīng)對這些挑戰(zhàn)的。
這張圖展示了百度智能云 —— 雙引擎 GPU 容器虛擬化架構(gòu)。
這里強調(diào)容器,因為我們相信,未來 AI 全鏈路應(yīng)用會逐步收斂到云原生平臺,實現(xiàn)全容器化開發(fā)、訓(xùn)練、推理。據(jù) Gartner 調(diào)研顯示,2023 年 70% 的 AI 任務(wù)將會容器化部署。百度內(nèi)部容器化從 2011 年就開始了,目前已經(jīng)有 10 余年的部署和優(yōu)化經(jīng)驗,我們也致力于將這部分真刀真槍打磨出來的產(chǎn)品能力和優(yōu)化經(jīng)驗貢獻給社區(qū)和廣大的用戶。
這里還強調(diào)了雙引擎。在整體架構(gòu)中我們采用了用戶態(tài)和內(nèi)核態(tài)兩套隔離引擎,以滿足用戶對隔離性、性能、效率等多方面不同側(cè)重的需求。
在隔離引擎之上,是資源池化層,該層次基于我們對軟硬件體系深刻理解,逐步實 AI 加速資源的解耦、拉遠和池化,是我們面向未來基礎(chǔ)設(shè)施打造的池化抽象層。
在資源池化層之上,是 Matrix / k8s 統(tǒng)一資源調(diào)度層(這里的 Matrix 是百度廠內(nèi)的容器化調(diào)度系統(tǒng)),在調(diào)度機制之上,我們會根據(jù)不同業(yè)務(wù)場景,抽象出來多種混布策略,包括共享混布,搶占混布,分時混布,潮汐混布等。這些混布策略,后續(xù)實踐部分會詳細展開。
依托于資源隔離和資源調(diào)度之上的是 AI 業(yè)務(wù)的全鏈路場景,包括模型開發(fā)、模型訓(xùn)練、在線推理。
接下來會分別給大家分享用戶態(tài)和內(nèi)核態(tài)隔離引擎的實現(xiàn)。
下圖是用戶態(tài)隔離引擎核心架構(gòu)示意圖。位于架構(gòu)圖最上的是用戶應(yīng)用,這里包含了各類常用框架,如PaddlePaddle、TensorFlow、PyTorch等。
位于用戶應(yīng)用之下的是一系列的 API Hook 接口,也是基于這套接口我們可以實現(xiàn) GPU 資源的本地使用和遠程掛載。通過替換框架依賴的底層動態(tài)庫,實現(xiàn)資源的控制和隔離。需要重點說明的是,該方案對于應(yīng)用是完全透明的,必要的庫替換操作已經(jīng)由容器引擎和調(diào)度部分自動完成。
CUDA API 在 Hook 之后會通過兩個通路最終到達執(zhí)行器。在這里,絕大多數(shù) API ,如設(shè)備管理 API 經(jīng)過 Hook 之后不做任何操作直接 pass-through 給執(zhí)行器執(zhí)行。少數(shù)和資源申請相關(guān)的 API 會經(jīng)過一層攔截,通過這層攔截實現(xiàn)用戶態(tài)虛擬化的一系列功能。這層的邏輯實現(xiàn)得足夠高效,對性能的影響幾乎忽略不計。
目前用戶態(tài)隔離引擎可以提供豐富的隔離和控制功能,包括基礎(chǔ)的顯存隔離、算力隔離。我們還擴展了很多高級功能:編碼器隔離、高優(yōu)搶占、顯存超發(fā)、顯存池化等等。?
用戶態(tài)方案的優(yōu)點是性能好,長尾延遲低,適合追求機制性能、極致效率的業(yè)務(wù)場景,如延遲敏感的在線推理業(yè)務(wù)。?
在隔離的基礎(chǔ)上,我們提供遠程功能,遠程的引入將大大提升資源配置的靈活度和使用效率,這一點我們將在本文最后展開。
本次分享是一次技術(shù)分享,這里用少量篇幅展開一下遠程技術(shù)的重點和難點,希望能激發(fā)大家的業(yè)務(wù)思路和技術(shù)討論。
根據(jù)我們在前文虛擬化挑戰(zhàn)中講到的軟硬件技術(shù)棧, GPU 的遠程訪問大致上也可以在硬件鏈路層、驅(qū)動層、運行時層和用戶層實現(xiàn),但經(jīng)過深入的技術(shù)分析并結(jié)合對業(yè)務(wù)場景的理解,我們認為目前最適合的還是運行時層。
確定運行時層技術(shù)路線,如何實現(xiàn)?技術(shù)的重點是什么?我們認為主要是語義一致性問題。基于運行時的遠程,需要把原始的 local 進程拆分為 client、 server 兩個進程。CUDA 運行時是閉源的,內(nèi)部實現(xiàn)邏輯無從探究。如何保證拆分進程后仍保持原有的程序邏輯和 API 語義,這里我們用一對一線程模型保證 API 內(nèi)部的邏輯和語義對齊。
遠程實現(xiàn)的難點是 API 繁多的問題,運行時除了 libcudart.so 這個動態(tài)庫,還涉及cuDNN、cuBLAS、cuFFT 等一系列動態(tài)庫和 API,涉及數(shù)千個不同的 API 接口。我們用編譯技術(shù)實現(xiàn)了頭文件的自動解析和代碼的自動生成,并通過逆向技術(shù)完成了隱藏API的解析。
解決遠程方案 0-1 適配之后,接下來的向后兼容性其實是比較好解決的。目前看來 CUDA API 相對穩(wěn)定,新版本只需要少量增量適配即可。
上面多次提到空分復(fù)用和時分復(fù)用。這里做一下詳細的解釋:
- 時分復(fù)用:顧名思義,是時間片層面的復(fù)用。這里與 CPU 的進程調(diào)度類似,在單一時間片內(nèi),只有一個 GPU 進程在運行。多個 GPU 進程之間在微觀層面上是交替運行的,只能成為并發(fā)(concurrent)。這也導(dǎo)致,在某一時間片內(nèi),如果該進程無法很好的利用計算資源,這些計算資源就是浪費掉的。
- 空分復(fù)用:與時分復(fù)用不同,空分復(fù)用時,在某一微觀時刻,多個進程是可以同時運行在一個 GPU 上的,只要這個 GPU 的資源沒有用滿,其它進程的 Kernel 就可以發(fā)射上來,兩個進程的 Kernel 在微觀層面上是交織運行的,真正實現(xiàn)了并行(parallel),進一步利用 GPU 資源。
如綜述部分介紹,當前常見的虛擬化方式,包括內(nèi)核態(tài)虛擬化、NVIDIA vGPU虛擬化,在底層實際都是基于時間片輪轉(zhuǎn)的時分復(fù)用方案。?
NV 面向多進程并發(fā)場景推出了 MPS ——多進程服務(wù)解決方案,該方案可以做到空分復(fù)用,是目前看到同時兼顧效率與性能的方案。?
這里簡單介紹一下 MPS,MPS 相當于把兩個進程的上下文融合成了一個進程,融合后的進程將之前兩個進程的 Kernel 交織到一起進行發(fā)射。這樣做有兩個好處:
- 進程之間無需上下文切換,減少了上下文切換的開銷。
- 同一時刻,不同進程的 kernel 交織,提升了資源空間利用率。
說到 MPS,不得不提被人詬病的一個缺點——故障隔離問題。?
如何解決這個 MPS 穩(wěn)定性問題的呢?百度智能云結(jié)合調(diào)度、容器引擎、業(yè)務(wù)?;钐岢鲆徽走M程融合共享方案。
- 通過 kill 命令重定向?qū)崿F(xiàn)業(yè)務(wù)進程優(yōu)雅退出
- 通過 MPS 狀態(tài)檢測機制實現(xiàn)健康檢查和假死檢測
- 通過服務(wù)?;顚崿F(xiàn)用戶進程自動重啟
該方案已經(jīng)覆蓋商業(yè)(延遲敏感型重要業(yè)務(wù))90%+ 資源,并長期運行超兩年的時間,在提供極致性能的同時,相信能夠滿足絕大多數(shù)用戶對穩(wěn)定性的需求。
隨著 MPS 的接受程度越來越高,NV 也不斷增強 MPS 的穩(wěn)定性。這里可以提前透露一個好消息,NV 在今年下半年會在 MPS 穩(wěn)定性上大幅度增強,包括假死狀態(tài)檢測,進程優(yōu)雅退出這些功能都會成為 MPS 產(chǎn)品的一部分,MPS 的穩(wěn)定性和易用性會進一步提升。
在介紹高優(yōu)搶占功能之前,先和大家分享一下高優(yōu)搶占的業(yè)務(wù)場景。根據(jù)我們和廠內(nèi)外不同用戶的討論,大多數(shù) AI 應(yīng)用生產(chǎn)環(huán)境中按延遲敏感程度可以分為在線、近線、離線這三類任務(wù)。
- 在線任務(wù),對延遲最高,一般是實時響應(yīng)用戶請求的推理任務(wù);
- 近線任務(wù),是一般是批處理任務(wù),對單條日志的延遲沒有要求,但對一批數(shù)據(jù)的完成時間有小時到分鐘級不等的要求;
- 離線任務(wù),對延遲無要求,只關(guān)注吞吐,一般是模型訓(xùn)練類任務(wù)。
如果我們把延遲敏感型任務(wù)定義為高優(yōu)任務(wù),把延遲不敏感的近線離線任務(wù)定義為低優(yōu)任務(wù)。并在兩類任務(wù)混布時根據(jù)任務(wù)優(yōu)先級不同定義不同的 kernel 發(fā)射優(yōu)先級,就是我們上面提到的高優(yōu)搶占功能。?
實現(xiàn)原理如下圖所示,用戶態(tài)隔離引擎為高優(yōu)任務(wù)和低優(yōu)任務(wù)各自維護了一個邏輯上的 kernel 隊列。當整體負載較低時,允許兩個隊列同時發(fā)射 kernel,這時兩個隊列的 kernel 是交織在一起運行的。一旦負載增高,分級發(fā)射模塊就會第一時間 pending 低優(yōu)隊列的發(fā)射,從而保證高優(yōu)任務(wù)的執(zhí)行延遲。
該功能的優(yōu)勢是保證離線吞吐的同時,減少甚至避免了在線任務(wù)的影響。
同理,我們先介紹分時混布的定義和場景。
分時混布,在混布模式上有點像時間片輪轉(zhuǎn)的共享混布。不同之處在于分時混布針對顯存不提出了顯存 swap 方案,這樣在顯存長期占用但算力間歇使用或偶爾觸發(fā)的場景就派上了用場。當進程需要算力時獲取顯存的訪問權(quán)限,當進程完成運算后釋放顯存的訪問權(quán)限,讓其它等待該權(quán)限的進程獲得運行機會,讓間歇閑置的 GPU 資源得到充分利用。
分時混布的核心技術(shù)是顯存 swap。我們可以類比 CPU 的內(nèi)存 swap,當某一進程的內(nèi)存不夠用了,系統(tǒng)會根據(jù)一定的策略換出一部分系統(tǒng)內(nèi)存資源到磁盤,從而騰挪出空間給在運行的進程使用。
顯存 swap 的實現(xiàn)原理如下圖所示。我們在顯存的物理地址上維護了一個顯存池,上層通過資源鎖來確定哪個進程有權(quán)限使用 GPU。當進程獲得鎖時,顯存便會從內(nèi)存或磁盤搬運到物理顯存池中,進一步映射到虛擬地址空間供進程使用。當進程釋放鎖時,會保留進程的虛擬顯存空間,將物理顯存搬移到內(nèi)存或磁盤上。該鎖是互斥的,只有一個進程可以獲得鎖,其它的進程 pending 在等待隊列上,以 FIFO 的方式依次獲得資源鎖。
上面介紹了用戶態(tài)隔離引擎的功能實現(xiàn),在實際應(yīng)用中,性能如何,對用戶的影響如何?這里我們直接上測試數(shù)據(jù)。
下圖是我們在公開測試集 MLPerf 上選擇典型模型 ResNet-50 Server 場景下的數(shù)據(jù)對比。圖中的柱子從左至右依次表示獨占、裸混、用戶態(tài)隔離、內(nèi)核態(tài)隔離下的性能。
左圖是平均吞吐對比,在推理場景下請求是間歇觸發(fā)的,我們可以看到,無論何種方案在吞吐下都能直接達到發(fā)壓值。這里想說明,推理場景下吞吐并不能很好的展示虛擬化性能,在生產(chǎn)環(huán)境中落地時應(yīng)該更多的關(guān)注延遲。
右圖是 P99 分位延遲的對比??梢钥吹?,在低壓力下(QPS = 40)用戶態(tài),裸混對長尾延遲的影響基本一致,內(nèi)核態(tài)由于采用了時分復(fù)用,對長尾延遲影響稍大。我們繼續(xù)增大壓力,在 QPS = 60 時,用戶態(tài)的優(yōu)勢就顯現(xiàn)了,空分復(fù)用大大降低了對長尾延遲的影響。隨著壓力的進一步加大,用戶態(tài)進程融合方案甚至比其它混布方式有數(shù)量級的提升。
盡管長尾延遲控制不如用戶態(tài),但在隔離性方面,內(nèi)核態(tài)具備優(yōu)勢,更側(cè)重于對隔離要求有強訴求的場景。?
下面我們來了解下內(nèi)核態(tài)隔離引擎的技術(shù)實現(xiàn)。
首先來看內(nèi)核態(tài)虛擬化實現(xiàn)的特點,包括如下:
內(nèi)核態(tài)實現(xiàn);隔離性好:支持顯存,算力和故障隔離;顯存 MB 級隔離;算力 1% 級分配;支持 P4,V100,T4,A100/A10/A30 等主流 GPU;支持 410 到 510 GPU 驅(qū)動版本;用戶態(tài)運行環(huán)境無需任何改變;支持容器化部署。
不同于用戶態(tài)的實現(xiàn),內(nèi)核態(tài)虛擬化對 GPU 的隔離功能都是在內(nèi)核態(tài)實現(xiàn)。下圖的左半部分是我們內(nèi)核態(tài)虛擬化實現(xiàn)的一個架構(gòu)圖,從底層到上層,分別是 GPU 硬件,內(nèi)核層,用戶層。
硬件層面就是我們的 GPU,這個 GPU 可以裸機的 GPU,也可以是透傳的 GPU。
內(nèi)核層的底下是 GPU 原有的驅(qū)動,它實際控制著 GPU 的功能,真正操作 GPU 的都是這個驅(qū)動,然后 GPU 驅(qū)動上面就是我們實現(xiàn)的 GPU 虛擬化的一個內(nèi)核模塊,也就是 GPU 攔截驅(qū)動,就是黃色的部分,包含三部分功能,包括顯存攔截,算力攔截和算力調(diào)度。分別實現(xiàn)的顯存隔離,算力隔離。
用戶層,首先是攔截接口。這個接口是由攔截模塊提供的,分為兩部分:一部分是設(shè)備文件接口,一部分是配置攔截模塊的接口。設(shè)備文件是提供給容器的,我們先來看容器。容器上面是應(yīng)用,底下是 cuda runtime,在下面是cuda 底層庫,包括 driver api/nvml api 等。通過把我們的設(shè)備文件提供給容器作為假的設(shè)備文件,那么上層 cuda 訪問時,就訪問的是我們的設(shè)備文件,這樣就完成了 cuda 底層庫對訪問 GPU驅(qū)動的攔截。
?我們在內(nèi)核的攔截模塊,會攔截所有訪問的系統(tǒng)調(diào)用,攔截并解析,然后把真正的訪問,重定向到真正的 GPU 底層驅(qū)動。GPU底層驅(qū)動處理完后,把結(jié)果返回給我們的攔截模塊,攔截模塊再次進行處理,最后把結(jié)果返回給容器里的底層庫。
簡單來說,就是通過模擬設(shè)備文件來攔截底層庫對 GPU 驅(qū)動的訪問,通過攔截、解析和注入等操作,完成顯存和算力等攔截。?
目前顯存隔離是通過攔截所有顯存相關(guān)的系統(tǒng)調(diào)用來實現(xiàn),主要包括顯存信息,顯存分配和顯存釋放等。而且當前顯存隔離只能靜態(tài)設(shè)置,不能動態(tài)改變。相對用戶態(tài)可以支持顯存超發(fā),內(nèi)核態(tài)還無法做到顯存超發(fā)。
算力隔離方面,通過攔截進程的 CUDA Context 來獲取相關(guān)信息。調(diào)度對象是進程相關(guān)的 CUDA Context。CUDA Context 對應(yīng)的算力資源包括計算資源(Execution)和內(nèi)存拷貝(Copy)資源。每個 GPU 有一個內(nèi)核線程進行此 GPU 上所有 CUDA Context 的調(diào)度。
我們實現(xiàn)了 4 種內(nèi)核態(tài)算力調(diào)度算法:
- Fixed Share:每個 POD 分配固定的算力資源,即整個 GPU 的算力固定分為 n 份,每個 POD 分 1/n 的算力。
- Equal Share:所有活躍的 POD 平分算力資源,即活躍的 POD 數(shù)為 n,每個 POD 分 1/n 的算力。
- Weight Share:每個 POD 按照權(quán)重分配算力資源,即整個 GPU 的算力按照權(quán)重值分配給每個 POD。不管 POD 是否有業(yè)務(wù)負載,都按照權(quán)重分配算力。
- Burst Weight Share:活動的 POD 按照權(quán)重分配算力資源,即每個 POD 分配權(quán)重值,活躍的POD按照權(quán)重的比值分配算力。
內(nèi)核態(tài)因為是通過時間片進行算力調(diào)度,所以對于延遲敏感型的業(yè)務(wù)不是很友好。我們特別開發(fā)了在離線混部技術(shù),通過在線業(yè)務(wù)和離線業(yè)務(wù)進行混部,大大提高在線業(yè)務(wù)的響應(yīng)速度的同時,也能讓離線業(yè)務(wù)共享 GPU 的算力資源,達到提高 GPU 資源使用率的目標。我們在離線混部的特點是:
- 在線 POD:推理任務(wù),平時占用少量算力。
- 離線 POD:訓(xùn)練任務(wù),平時占用大部分算力。
當在線 POD 有任務(wù)負載時,立刻搶占離線 POD,占用全部算力提供推理服務(wù)。當任務(wù)負載結(jié)束時,釋放算力給離線 POD。?
以下是內(nèi)核態(tài)算力隔離的評測結(jié)果:
測試環(huán)境是單卡 V100 SXM2 16G,訓(xùn)練場景下測試吞吐,測試采用 horovod 框架,模型為 resnet50。
POD 1 和 POD 2 的 weight 比值為 1:2。
上面的圖的結(jié)果,可以看出,POD 1 和 POD 2 吞吐比值在 45~50%,大概就是 1/2 這樣一個結(jié)果,符合我們預(yù)設(shè)的值。同時 POD SUM 較 Native 有 2~4%的損耗,因為算力隔離需要對 Cuda Context 進行切換操作,不可避免有損耗,但是我們的損耗在 5% 以內(nèi),可以說在容忍范圍中。
我們比較一下內(nèi)核態(tài)和用戶態(tài)的特點。
故障隔離方面,內(nèi)核態(tài)較用戶態(tài)有優(yōu)勢,而且內(nèi)核態(tài)不需要對底層庫進行替換。用戶態(tài)算力調(diào)度采用時分加空分復(fù)用,內(nèi)核態(tài)采用的時分復(fù)用。用戶態(tài)高級功能包括在離線混部,顯存超發(fā)到內(nèi)存、編解碼實例(將 AI 加速卡的編碼跟解碼資源獨立分配),內(nèi)核態(tài)我們也支持在離線混部等。
如何利用虛擬化技術(shù)提升 AI 場景中 GPU 的利用效率,下面結(jié)合廠內(nèi)實際案例分享一下大規(guī)模 AI 場景下的最佳實踐。
我們首先看一個推理服務(wù)中的典型場景。由于模型本身架構(gòu)或者是服務(wù)延遲要求較高,某些任務(wù)只能在 batchsize 很小,甚至為 batchsize 為 1 的配置下運行。直接導(dǎo)致 GPU 利用率長期偏低,甚至峰值利用率僅有 10%。
這種場景下,首先應(yīng)該想到的是多個低利用率任務(wù)之間進行混布。
我們把這種混布策略歸納為共享混布。無論在開發(fā)、訓(xùn)練、還是推理場景,在多個低利用率任務(wù)之間,我們都可以采用共享混布。
結(jié)合上面提到過的進程融合技術(shù),可以在保證服務(wù)延遲的基礎(chǔ)上,實現(xiàn)2個實例甚至多實例的共享混布,資源利用率提升 2 倍以上。?
于此同時,多數(shù) GPU 上都有獨立的編解碼資源。在大多數(shù)場景下,如左下圖所示,該資源長期閑置。我們可以在共享計算資源的基礎(chǔ)上,再混布一個編碼或解碼實例,進一步提升資源效能,激活閑置資源。
推理服務(wù)一個典型的負載模式是一天中峰谷波動明顯,且會出現(xiàn)不可預(yù)期的短時間流量激增。這就出現(xiàn)了雖然峰值很高,但平均利用率卻很差,均值經(jīng)常不到 30% 甚至 20%。
這類波動明顯,短時激增的服務(wù)如何進行效率優(yōu)化呢?我們提出了搶占混布策略。
搶占混布是在峰值較高且延遲敏感的高優(yōu)業(yè)務(wù)上混布一個延遲不敏感的低優(yōu)任務(wù)。這里的高優(yōu)、低優(yōu)是由用戶自己定義,并且在申請資源時顯式聲明的。我們在百度內(nèi)部實踐中,會將近線、離線的刷庫或訓(xùn)練任務(wù)定義為低優(yōu),這類業(yè)務(wù)對吞吐有一定的要求,對延遲基本上沒有要求。?
利用虛擬化功能中的高優(yōu)搶占機制,高優(yōu)任務(wù)時刻掌握占用資源的主動權(quán)。當流量處于波谷時,整卡的負載不高,低優(yōu)任務(wù)可以正常運行,一旦流量處于波峰或者出現(xiàn)短時激增,高優(yōu)搶占機制可以實時感知并且在 kernel 粒度上進行算力的搶占,此時低優(yōu)任務(wù)會被限流甚至完全 pending,保障高優(yōu)任務(wù)的服務(wù)質(zhì)量。
這種混布模式下可能會出現(xiàn)顯存不足的情況,此時算力可能還有很大冗余。針對這類場景,我們提供了隱式的顯存超發(fā)機制。用戶可以通過環(huán)境變量對低優(yōu)任務(wù)進行顯存超發(fā),混布更多的實例,確保隨時有算力填充利用率的波谷,實現(xiàn)整體利用效率最大化。
第三類業(yè)務(wù)場景大家可能并不陌生,這就是顯存常駐、算力間歇性觸發(fā)場景。典型的代表業(yè)務(wù)是開發(fā)任務(wù)和在線訓(xùn)練。
這里以在線訓(xùn)練為例。我們知道很多模型需要根據(jù)用戶每日甚至每時的數(shù)據(jù)進行在線更新,例如推薦模型,這就需要用到在線訓(xùn)練。和吞吐實時打滿的離線訓(xùn)練不同,在線訓(xùn)練需要積累一批數(shù)據(jù)后觸發(fā)一次訓(xùn)練。百度內(nèi)部,典型的模式可能是 15 分鐘到達一批數(shù)據(jù),但真正的訓(xùn)練時間只有 2 到 3 分鐘,剩余的時間里這個訓(xùn)練進程就常駐顯存等在那里,直至下一批數(shù)據(jù)從上游抵達。在此期間,利用率長期為 0,造成了大量的資源浪費。
這類任務(wù)由于顯存基本占滿,無法使用上面提到的共享混布或搶占混布。結(jié)合之前提到的顯存 swap 機制,我們提出了分時混布策略。
分時混布類似于時間片輪轉(zhuǎn)的共享混布,但此時顯存也會隨著計算的上下文一同被換入換出。由于底層的虛擬化層無法感知業(yè)務(wù)何時需要計算,我們針對每張 GPU 卡,維護了一個全局的資源鎖。并封裝了相應(yīng)的 C++ 和 Python 接口供用戶調(diào)用。用戶只需要在需要計算的時候申請這把鎖,顯存就會從其它空間自動換入到顯存空間;在計算完成后釋放這把鎖,對應(yīng)的顯存會被換出到內(nèi)存或者磁盤空間。利用這個簡單的接口,用戶可以實現(xiàn)多個任務(wù)分時獨占 GPU。在線訓(xùn)練場景中,使用分時混布,可以在拉升整體利用率的基礎(chǔ)上實現(xiàn)最高 4/5 的資源節(jié)省。
上面提到的三個場景的最佳實踐,在百度內(nèi)部業(yè)務(wù)上已經(jīng)實現(xiàn)了長期驗證和規(guī)模落地。相關(guān)功能也已經(jīng)上線百度百舸·AI異構(gòu)計算平臺,大家可以即刻申請和試用。
這里我再用三分鐘左右的篇幅講一下還在內(nèi)部驗證中的功能,這些功能將會近期完成在百度百舸平臺的上線,進一步解決在大規(guī)模AI場景下常見的配比不均、供需失衡、資源碎片等問題。
做基礎(chǔ)架構(gòu)的同學(xué)一定會經(jīng)常聽到資源的解耦、池化這類概念。如何將池化概念落地,并轉(zhuǎn)化為實際生產(chǎn)力,是我們一直以來積極探索和推進的。早在 2015 年,我們就實現(xiàn)了業(yè)界首個基于 PCIe Fabric 方案的硬件池化方案,并在百度內(nèi)部實現(xiàn)規(guī)?;涞?,這就是剛才提到的 X-MAN 1.0(目前已經(jīng)演進到 4.0)。通過 PCIe Fabric 網(wǎng)絡(luò)配置 CPU 和 GPU之間的互聯(lián),實現(xiàn)資源的動態(tài)分配,解決各類場景下的配比問題。受限于硬件連接和協(xié)議的限制,該方案只能解決機柜內(nèi)部的池化。
軟件層池化是我們認為更靈活的技術(shù)方案。隨著數(shù)據(jù)中心網(wǎng)絡(luò)不斷升級,100G 甚至200G 的網(wǎng)絡(luò)未來會成為基礎(chǔ)設(shè)施的標配,高速網(wǎng)絡(luò)為資源池化提供了通信高速路。
資源的解耦和池化讓業(yè)務(wù)擁有更大的靈活度,也為效能優(yōu)化提供了更大的想象空間。例如 CPU 和 GPU 之間的配比問題,開發(fā)場景中長期資源占用供需失衡效率低下的問題,訓(xùn)練場景中資源碎片任務(wù)阻塞問題、設(shè)備異常訓(xùn)練重啟問題,這類場景都能在池化及衍生方案中得到解決。
最后,上述分享的所有的虛擬化技術(shù)和最佳實踐,都已經(jīng)上線百度百舸·AI異構(gòu)計算平臺。在百度智能云官網(wǎng)搜索“百度百舸”,即刻加速 AI 任務(wù),激發(fā)業(yè)務(wù)想象!
Q & A 精選
Q:一般資源通過 namespace 和 cgroup 來實現(xiàn)容器化。請問 GPU 通過什么技術(shù)實現(xiàn)資源控制的?
A:namespace 和 cgroup 都是內(nèi)核提供的機制,本質(zhì)上還要依賴于硬件提供的相關(guān)能力。這一點在目前 GPU 上是不存在的,GPU 目前并長期是閉源狀態(tài),這些能夠 upstream 到內(nèi)核主線的功能只有硬件提供商有能力提供。當前三方的方案都是在用戶態(tài)或內(nèi)核態(tài)做的非標準實現(xiàn),暫時還沒有辦法納入 namespace 和 cgroup 范疇。但可以認為 GPU 虛擬化要實現(xiàn)的就是這些接口下面對應(yīng)的機制,至于是否能標準化是另外一個更大的問題。
Q:請問除了 GPGPU 的虛擬化技術(shù),咱們是否有開展 NPU 相關(guān)虛擬化技術(shù)?是否與 NV 技術(shù)棧進行解耦。謝謝!
A:我理解這里說的 NPU 應(yīng)該是 Network Processing Unit,泛指當前所有的 AI 加速硬件。我們正在做其它 AI 加速硬件的虛擬化適配。首先是昆侖芯,我們已經(jīng)在昆侖芯上做了上面提到虛擬化能力的適配。隨著場景的擴展,會不斷適配其它主流加速硬件。
Q :用戶態(tài)和內(nèi)核態(tài)是兩個不同的產(chǎn)品嗎?
A:是同一個產(chǎn)品,底層不同的實現(xiàn)方式,用戶接口層面是統(tǒng)一的。
Q :用戶態(tài)虛擬化能做到什么顆粒度?
A:算力做到 1% 粒度切分,顯存做到 1MB 切分。
Q :請問內(nèi)核態(tài)的虛擬化是否會造成較大的控制開銷?
A:內(nèi)核態(tài)虛擬化是基于時間分片的,這里的開銷是時間分片帶來的,精準的隔離必然會帶來算力的損失。如果是指對應(yīng)用性能帶來的開銷,確實內(nèi)核態(tài)會比用戶態(tài)大一些。
Q :按照時分實現(xiàn)的方案,在線推理感覺還是自由競爭平均時間更快。
A:按照我們測試結(jié)果來看,性能由好變差依次為:進程融合,裸混(自由競爭),硬限隔離。
Q : GPU 這兩種虛擬化的方式可以在一個 k8s 集群共存嗎?
A:從機制和原理來講,是可以做到共存的。但目前從產(chǎn)品維度不想設(shè)計的這么復(fù)雜,所以還是分開的。如果后續(xù)業(yè)務(wù)有廣泛的訴求,我們會考慮推出類似共存的方案。
Q :請問可以詳細介紹下 k8s 的調(diào)度器如何擴展嗎?是否需要節(jié)點上的 agent 上報 GPU 拓撲和總量?
A:需要,這塊需要單機的 agent 上傳資源(包括顯存資源和算力資源)和拓撲信息。
Q :請問時分和空分的選擇上有什么建議嗎?
A: 延遲敏感型的在線推理任務(wù),建議選擇基于進程融合的空分方案。要求嚴格隔離的場景建議選擇時分方案。其它場景選擇兩者沒有區(qū)別。
Q :內(nèi)核態(tài)能支持到哪個 CUDA 版本?如果 NV 更新了,百度智能云的更新周期要多久?
A: 內(nèi)核態(tài)因為是在內(nèi)核做的虛擬化,對 CUDA 版本沒有特別要求,目前支持所有CUDA 版本。如果 NV 更新 CUDA,預(yù)期不需要做特別支持工作。
Q :使用內(nèi)核態(tài),需不需要使用專門的百度智能云提供的 OS 鏡像?專用的驅(qū)動程序?
A:內(nèi)核態(tài)不需要百度智能云專門提供 OS 鏡像。目前我們對 centos7 和 ubuntu 都做了支持。不過需要用我們自己的部署框架來使用。對容器鏡像沒有特別要求,都可以透明支持。
Q :是不是只有在公有云才能使用?能私有化部署嗎?
A:公有云和私有云都可以部署和使用。?