首頁  >  文章  >  科技週邊  >  技術解析與實作分享:雙引擎GPU容器虛擬化中的使用者態與核心態

技術解析與實作分享:雙引擎GPU容器虛擬化中的使用者態與核心態

王林
王林轉載
2023-04-23 15:40:101086瀏覽

如何讓硬體算力發揮最大效率,是所有資源營運商和使用者非常關注的問題。百度作為一家領先的 AI 公司,擁有可能是業界最全的 AI 應用場景。

在這篇文章中,將會和大家分享並討論 GPU 容器虛擬化在複雜AI場景中的解決方案和廠內的最佳實務。

下面這張圖片的左右兩部分,在不同場合下已經多次展示過,放到這裡主要想強調算力需求—— 硬體算力的指數型增長,與真實應用場景中利用率偏低資源浪費之間的矛盾。

左邊的部分是OpenAI 統計的數據,從2012 年以來,模型訓練所需的算力每3.4 個月翻一倍,截止到AlphaGoZero 這類的大模型,訓練算力已經增長了30 萬倍,而這種趨勢還在持續。一方面,隨著算力需求的成長,主流 AI 加速單元運算效能也以每兩年翻一倍的速率增加。另一方面,資源利用效率卻限制硬體效能的充分發揮。

右邊的部分是 Facebook 在 2021 年對資料中心 Machine Learning 負載分析的結果。大量的 AI 算力損失在故障、調度、時間片浪費、空間單元浪費等環節,真正的算力利用率不到 30%。我們相信,這也是國內各大基礎建設業者所面臨的現狀。

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

剛剛提到線上集群不到 30% 使用率可能不符合許多同學的認知。在線上的很多同學可能是模型和演算法的開發者。我們普遍的認知是,在訓練和測試過程中利用率可以保持很高的水平,甚至可以達到 100% 使用率。

但模型在生產環境上線,會受到很多約束,這些約束導致利用率遠遠達不到我們的預期。

下面我們用有限的篇幅總結一下主要的限制因素:

  • 模型特徵:每個模型網路不同,呼叫的底層算符組合不同,很大程度上會影響GPU 的使用率。
  • 服務SLA:不同場景下的服務需要不同的SLA,有的服務即時性要求較高,甚至需要嚴格控制在10ms 以內,那麼這些服務就不能透過增加batchsize 的方式提升使用率,甚至batchsize 只能為1。
  • 流量模式:不同模型演算法服務於不同的應用場景,例如 OCR 識別,可能在工作期間被頻繁調用。而語音辨識則較多的在通勤時間或娛樂休閒時才會被調用,這樣就導致了一天中 GPU 利用率的峰谷波動。
  • 最佳化效果:根據模型的迭代頻率以及覆蓋場景的不同,模型的最佳化粒度也不盡相同。可想而知,一個未經充分優化的模型利用率也很難達到較高的水準。
  • 容量冗餘:模型上線前都要經過詳細的容量規劃,最大流量是多少,是否需要多地域,在此過程中會預留難以忽略的容量冗餘,這些冗餘在平時也造成了算力的浪費。

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

在上面種種約束條件的限制下,真實生產環境的利用率可能是接下來我們要展示的。我們從複雜多變的線上生產環境中抽像出這幾種利用率模式。

  • 平均值偏低型:如左上圖,為一個真實的線上推理業務,由於模型特徵和服務 SLA 的限制,GPU 的峰值利用率只有 10%,平均利用率會更低。
  • 峰谷波動型:如左下圖,是典型的線上推理業務的利用率模式,服務在白天會達到高峰,在深夜至第二天早上是利用率的低谷,全天平均利用率只有20% 左右,低谷利用率只有10% 不到。
  • 短時間激增型:如右上圖,利用率曲線基本上與左下圖一致,但在夜間黃金時段會有兩個明顯的利用率高峰,高峰階段的利用率高達80%,為了滿足高峰階段的服務質量,該服務在部署過程中會預留不小的buffer,資源平均利用率也剛超過30%。
  • 週期觸發型:如右下圖,是典型線上訓練場景的利用模式,線上訓練任務介於離線訓練和線上推理之間,這是一種週期性批次的任務。例如每 15 分鐘會有一批資料到達,但這批資料的訓練只需要 2-3 分鐘,大量的時間 GPU 處於閒置狀態。

AI應用程式場景複雜多變,上面只是列舉了四個典型場景。如何在複雜場景中,平衡業務效能與資源效率,是我們在 GPU 虛擬化中遇到的第一個挑戰。

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

GPU 虛擬化流程中我們面臨的第二個挑戰就是缺乏完善的 GPU 隔離與混布機制。

我們以目前主流的 NVIDIA GPU 為例。典型的 AI 軟硬體生態都分為這樣幾個層次 ——應用 & 框架層,運行時層,驅動層,硬體層。

首先最上層是使用者的應用,這裡包含了各種常見的框架 PaddlePaddle、TensorFlow、PyTorch 等等。在應用層之下是硬體提供者封裝的 API 介面層,包含各類常用算符庫與硬體執行時間存取介面。在這層 API 介面之下,是與硬體溝通的驅動層,該層位於核心態,是直接與裝置溝通的軟體介面層。位於最底層是真正的 AI 加速硬件,負責算子的執行。

傳統的虛擬化方案,都會結合驅動核心態以及硬體虛擬化邏輯實作。這兩個層次是硬體提供者最核心的 IP,一般是閉源的。後續會提到,目前 GPU 原生的隔離機制在彈性和分配力道上都無法滿足雲端原生場景下的使用需求。

除了隔離機制,現有的混布機制也很難滿足複雜場景的需求,我們看到業界有很多共享調度的開源方案,這些開源方案只是從資源的層面把兩個任務簡單的調度到一張卡上。在實際場景中,簡單的共享會造成業務之間相互影響,長尾延遲甚至吞吐的惡化導致簡單共享無法真正應用於生產環境。

在上文利用率模式分析一節我們看到不同的業務,不同的場景下,利用率模式都不盡相同。如何抽象化業務場景,客製化混布方案,是生產環境落地的關鍵。

双引擎 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 架构与虚拟化历史脉络有了一些印象之后,我们来详细介绍下实现 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 虚拟化的挑战和现状,接下来我们看百度内部是如何应对这些挑战的。

这张图展示了百度智能云 —— 双引擎 GPU 容器虚拟化架构。

这里强调容器,因为我们相信,未来 AI 全链路应用会逐步收敛到云原生平台,实现全容器化开发、训练、推理。据 Gartner 调研显示,2023 年 70% 的 AI 任务将会容器化部署。百度内部容器化从 2011 年就开始了,目前已经有 10 余年的部署和优化经验,我们也致力于将这部分真刀真枪打磨出来的产品能力和优化经验贡献给社区和广大的用户。

这里还强调了双引擎。在整体架构中我们采用了用户态和内核态两套隔离引擎,以满足用户对隔离性、性能、效率等多方面不同侧重的需求。

在隔離引擎之上,是資源池化層,該層次基於我們對軟硬體體系深刻理解,逐步實AI 加速資源的解耦、拉遠和池化,是我們面向未來基礎設施打造的池化抽象層。

在資源池化層之上,是Matrix / k8s 統一資源調度層(這裡的Matrix 是百度廠內的容器化調度系統),在調度機制之上,我們會根據不同業務場景,抽象化多種混佈策略,包括共享混布,搶佔混布,分時混布,潮汐混布等。這些混布策略,後續實踐部分會詳細展開。

依託於資源隔離和資源調度之上的是 AI 業務的全鏈路場景,包括模型開發、模型訓練、線上推理。

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

接下來會分別跟大家分享使用者狀態和核心態隔離引擎的實作。

下圖是用戶態隔離引擎核心架構示意圖。位於架構圖最上的是使用者應用,這裡包含了各類常用框架,如PaddlePaddle、TensorFlow、PyTorch等。

位於用戶應用之下的是一系列的 API Hook 接口,也是基於這套接口我們可以實現 GPU 資源的本地使用和遠端掛載。透過替換框架依賴的底層動態函式庫,實現資源的控制和隔離。需要重點說明的是,該方案對於應用是完全透明的,必要的庫替換操作已經由容器引擎和調度部分自動完成。

CUDA API 在 Hook 之後會經過兩個路徑最終到達執行器。在這裡,絕大多數 API ,如設備管理 API 經過 Hook 之後不做任何操作直接 pass-through 給執行器執行。少數和資源申請相關的 API 會經過一層攔截,透過這層攔截實現使用者態虛擬化的一系列功能。這層的邏輯實現得夠高效,對效能的影響幾乎忽略不計。

目前使用者態隔離引擎可以提供豐富的隔離和控制功能,包括基礎的顯存隔離、算力隔離。我們也擴展了許多進階功能:編碼器隔離、高優搶佔、顯存超發、顯存池化等等。

使用者態方案的優點是效能好,長尾延遲低,適合追求機制效能、極致效率的業務場景,如延遲敏感的線上推理業務。

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

在隔離的基礎上,我們提供遠端功能,遠端的引入將大大提升資源配置的靈活度和使用效率,這一點我們將在本文最後展開。

這次分享是一次技術分享,這裡用少量篇幅展開一下遠端技術的重點和難點,希望能激發大家的業務思路和技術討論。

根據我們在前文虛擬化挑戰中講到的軟硬體技術棧, GPU 的遠端存取大致上也可以在硬體鏈路層、驅動層、運行時層和用戶層實現,但經過深入的技術分析並結合對業務場景的理解,我們認為目前最適合的還是運行時層。

確定運行時層技術路線,如何實現?技術的重點是什麼?我們認為主要是語意一致性問題。基於執行時期的遠端,需要把原始的 local 程序拆分為 client、 server 兩個程序。 CUDA 運行時是閉源的,內部實作邏輯無從探究。如何確保拆分進程後仍保持原有的程式邏輯和 API 語意,這裡我們用一對一執行緒模型保證 API 內部的邏輯和語意對齊。

遠端實現的困難是 API 繁多的問題,運行時除了 libcudart.so 這個動態庫,還涉及cuDNN、cuBLAS、cuFFT 等一系列動態庫和 API,涉及數千個不同的 API 介面。我們用編譯技術實現了頭檔的自動解析和程式碼的自動生成,並透過逆向技術完成了隱藏API的解析。

解決遠端方案 0-1 適配之後,接下來的向後相容性其實是比較好解決的。目前看來 CUDA API 相對穩定,新版只需要少量增量適配即可。

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

上面多次提到空分複用和時分複用。這裡做一下詳細的解釋:

  • 時分複用:顧名思義,是時間片層面的複用。這裡與 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 的穩定性和易用性會進一步提升。

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

在介紹高優搶佔功能之前,先和大家分享一下高優搶佔的業務場景。根據我們和廠內外不同使用者的討論,大多數 AI 應用程式生產環境中依延遲敏感程度可以分為線上、近線、離線這三類任務。

  • 線上任務,對延遲最高,一般是即時回應使用者請求的推理任務;
  • 近線任務,是一般是批次任務,對單一日誌的延遲沒有要求,但對一批資料的完成時間有小時到分鐘級不等的要求;
  • 離線任務,對延遲無要求,只關注吞吐,一般是模型訓練類任務。

如果我們把延遲敏感型任務定義為高優任務,把延遲不敏感的近線離線任務定義為低優任務。並且在兩類任務混佈時根據任務優先級不同定義不同的 kernel 發射優先級,就是我們上面提到的高優搶佔功能。

實作原理如下圖所示,用戶態隔離引擎為高優任務和低優任務各自維護了一個邏輯上的 kernel 佇列。當整體負載較低時,允許兩個佇列同時發射 kernel,這時兩個佇列的 kernel 是交織在一起運作的。一旦負載增高,分級發射模組就會第一時間 pending 低優隊列的發射,從而確保高優任務的執行延遲。

此功能的優勢是保證離線吞吐的同時,減少甚至避免了線上任務的影響。

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

同理,我們先介紹分時混布的定義與場景。

分時混布,在混布模式上有點像時間片輪轉的共享混布。不同之處在於分時混佈針對顯存不提出了顯存 swap 方案,這樣在顯存長期佔用但算力間歇使用或偶爾觸發的場景就派上了用場。當進程需要算力時取得顯存的存取權限,當進程完成運算後釋放顯存的存取權限,讓它等待該權限的進程獲得執行機會,讓間歇閒置的 GPU 資源得到充分利用。

分時混布的核心技術是顯存 swap。我們可以類比 CPU 的記憶體 swap,當某一進程的記憶體不夠用了,系統會根據一定的策略換出一部分系統記憶體資源到磁碟,從而騰出空間給在運作的行程使用。

顯存 swap 的實作原理如下圖所示。我們在顯存的實體位址上維護了一個顯存池,上層透過資源鎖來決定哪個程序有權限使用 GPU。當進程獲得鎖定時,顯存便會從記憶體或磁碟搬運到實體記憶體池中,進一步對應到虛擬位址空間供進程使用。當進程釋放鎖時,會保留進程的虛擬顯存空間,將實體顯存移到記憶體或磁碟上。此鎖是互斥的,只有一個程序可以獲得鎖,其它的程序 pending 在等待佇列上,以 FIFO 的方式依序取得資源鎖。

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

上面介紹了使用者態隔離引擎的功能實現,在實際應用中,效能如何,對使用者的影響如何?這裡我們直接上測試數據。

下圖是我們在公開測試集 MLPerf 上選擇典型模型 ResNet-50 Server 場景下的資料比較。圖中的柱子由左至右依序表示獨佔、裸混、用戶態隔離、內核態隔離下的效能。

左圖是平均吞吐對比,在推理場景下請求是間歇觸發的,我們可以看到,無論何種方案在吞吐下都能直接達到發壓值。這裡想說明,推理場景下吞吐並不能很好的展示虛擬化效能,在生產環境中落地時應該更多的關注延遲。

右圖是 P99 分位延遲的比較。可以看到,在低壓力下(QPS = 40)用戶態,裸混對長尾延遲的影響基本上一致,內核態由於採用了時分複用,對長尾延遲影響稍大。我們持續增大壓力,在 QPS = 60 時,用戶態的優勢就顯現了,空分複用大大降低了對長尾延遲的影響。隨著壓力的進一步加大,用戶態進程融合方案甚至比其它混佈方式有數量級的提升。

儘管長尾延遲控制不如使用者態,但在隔離性方面,內核態具備優勢,更著重於對隔離要求有強訴求的場景。

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

下面我們來了解下核心態隔離引擎的技術實作。

首先來看核心態虛擬化實作的特點,包括如下:

核心態實作;隔離性好:支援顯存,算力與故障隔離;顯存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 驅動的訪問,透過攔截、解析和注入等操作,完成顯存和算力等攔截。

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

目前顯存隔離是透過攔截所有顯存相關的系統呼叫來實現,主要包括顯存訊息,顯存分配和顯存釋放等。而且目前顯存隔離只能靜態設置,不能動態改變。相對使用者態可以支援顯存超發,核心態還無法做到顯存超發。

算力隔離方面,透過攔截進程的 CUDA Context 來取得相關資訊。調度物件是進程相關的 CUDA Context。 CUDA Context 對應的算力資源包括計算資源(Execution)和記憶體拷貝(Copy)資源。每個 GPU 都有一個核心執行緒進行此 GPU 上所有 CUDA Context 的調度。

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

我們實作了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 的算力資源,達到提高GPU 資源使用率的目標。我們在離線混部的特點是:

  • 線上 POD:推理任務,平時佔用少量算力。
  • 離線 POD:訓練任務,平時佔用大部分算力。

當線上 POD 有任務負載時,立刻搶佔離線 POD,佔用全部算力提供推理服務。當任務負載結束時,釋放算力給離線 POD。

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

以下是內核態算力隔離的評測結果:

測試環境是單卡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% 以內,可以說在容忍範圍中。

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

我們比較核心態和使用者狀態的特性。

故障隔離方面,核心態較使用者態有優勢,且核心態不需要對底層函式庫進行替換。使用者態算力調度採用時分加空分複用,內核態採用的時分複用。使用者態進階功能包含在離線混部,顯示超發到記憶體、編解碼實例(將 AI 加速卡的編碼跟解碼資源獨立分配),核心態我們也支援在離線混部等。

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

如何利用虛擬化技術提升 AI 場景中 GPU 的利用效率,以下結合廠內實際案例分享大規模 AI 場景下的最佳實務。

我們先來看一個推理服務中的典型場景。由於模型本身架構或服務延遲要求較高,某些任務只能在 batchsize 很小,甚至為 batchsize 為 1 的配置下運作。直接導致 GPU 利用率長期偏低,甚至峰值利用率僅 10%。

這種場景下,首先應該想到的是多個低利用率任務之間進行混布。

我們把這個混布策略歸納為共享混布。無論在開發、訓練、或推理場景,在多個低利用率任務之間,我們都可以採用共享混布。

結合上述提到的進程融合技術,可以在保證服務延遲的基礎上,實現2個實例甚至多實例的共享混布,資源利用率提升 2 倍以上。

於同時,多數 GPU 上都有獨立的編解碼資源。在大多數場景下,如左下圖所示,此資源長期閒置。我們可以在共享運算資源的基礎上,再混佈一個編碼或解碼實例,進一步提升資源效能,啟動閒置資源。

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

推理服務一個典型的負載模式是一天中峰谷波動明顯,且會出現不可預期的短時間流量激增。這就出現了雖然高峰很高,但平均利用率卻很差,平均值經常不到 30% 甚至 20%。

這類波動明顯,短時激增的服務如何進行效率最佳化呢?我們提出了搶佔混布策略。

搶佔混布是在峰值較高且延遲敏感的高優業務上混佈一個延遲不敏感的低優任務。這裡的高優、低優是由使用者自己定義,並且在申請資源時明確聲明的。我們在百度內部實踐中,會將近線、離線的刷庫或訓練任務定義為低優,這類業務對吞吐有一定的要求,對延遲基本上沒有要求。

利用虛擬化功能中的高優搶佔機制,高優任務時刻掌握佔用資源的主動權。當流量處於波谷時,整卡的負載不高,低優任務可以正常運行,一旦流量處於波峰或出現短時激增,高優搶佔機制可以實時感知並且在kernel 粒度上進行算力的搶佔,此時低優任務會被限流甚至完全pending,保障高優任務的服務品質。

這種混布模式下可能會出現顯存不足的情況,此時算力可能還有很大冗餘。針對這類場景,我們提供了隱性的顯存超發機制。使用者可以透過環境變數對低優任務進行顯存超發,混佈更多的實例,確保隨時有算力填充利用率的波谷,實現整體利用效率最大化。

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

第三類業務場景大家可能不陌生,這就是顯存常駐、算力間歇性觸發場景。典型的代表業務是開發任務和線上訓練。

這裡以線上訓練為例。我們知道許多模型需要根據使用者每日甚至每時的資料進行線上更新,例如推薦模型,這就需要用到線上訓練。和吞吐即時打滿的離線訓練不同,線上訓練需要累積一批資料後觸發一次訓練。百度內部,典型的模式可能是 15 分鐘到達一批數據,但真正的訓練時間只有 2 到 3 分鐘,剩餘的時間裡這個訓練進程就常駐顯存等在那裡,直至下一批數據從上游抵達。在此期間,利用率長期為 0,造成了大量的資源浪費。

這類任務由於顯存基本佔滿,無法使用上述的共享混布或搶佔混佈。結合先前提到的顯存 swap 機制,我們提出了分時混布策略。

分時混布類似時間片輪換的共享混布,但此時顯存也會隨著計算的上下文一同被換入換出。由於底層的虛擬化層無法感知業務何時需要運算,我們針對每張 GPU 卡,維護了一個全域的資源鎖。並封裝了對應的 C 和 Python 介面供使用者呼叫。使用者只需要在需要計算的時候申請這把鎖,顯存就會從其它空間自動換入到顯存空間;在計算完成後釋放這把鎖,對應的顯存會被換出到內存或磁碟空間。利用這個簡單的接口,使用者可以實現多個任務分時獨佔 GPU。在線上訓練場景中,使用分時混布,可以在拉升整體利用率的基礎上實現最高 4/5 的資源節省。

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

上面提到的三個場景的最佳實踐,在百度內部業務上已經實現了長期驗證和規模落地。相關功能也已經上線百度百舸AI異構運算平台,大家可以即時申請試用。

這裡我再用三分鐘左右的篇幅講一下還在內部驗證中的功能,這些功能將會近期完成在百度百舸平台的上線,進一步解決在大規模AI場景下常見的配比不均、供需失衡、資源碎片等問題。

做基礎架構的同學一定會常常聽到資源的解耦、池化這類概念。如何將池化概念落地,並轉化為實際生產力,是我們一直以來積極探索和推進的。早在 2015 年,我們就實現了業界首個基於 PCIe Fabric 方案的硬體池化方案,並在百度內部實現規模化落地,這就是剛才提到的 X-MAN 1.0(目前已經演進到 4.0)。透過 PCIe Fabric 網路配置 CPU 與 GPU之間的互聯,實現資源的動態分配,解決各類場景下的配比問題。受限於硬體連接和協定的限制,此方案只能解決機櫃內部的池化。

軟體層池化是我們認為更靈活的技術方案。隨著資料中心網路不斷升級,100G 甚至200G 的網路未來將成為基礎設施的標配,高速網路為資源池化提供了通訊高速路。

資源的解耦和池化讓業務擁有更大的靈活度,也為效能優化提供了更大的想像空間。例如CPU 與GPU 之間的配比問題,開發場景中長期資源佔用供需失衡效率低下的問題,訓練場景中資源碎片任務阻塞問題、設備異常訓練重啟問題,這類場景都能在池化及衍生方案中得到解決。

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

最後,上述分享的所有的虛擬化技術和最佳實踐,都已經上線百度百舸·AI異構計算平台。在百度智慧雲端官網搜尋“百度百舸”,即時加速 AI 任務,激發業務想像!

双引擎 GPU 容器虚拟化,用户态和内核态的技术解析和实践分享

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:公有雲和私有雲都可以部署和使用。

以上是技術解析與實作分享:雙引擎GPU容器虛擬化中的使用者態與核心態的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:51cto.com。如有侵權,請聯絡admin@php.cn刪除