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