vivo 軒轅文件系統(tǒng):AI 計算平臺存儲性能優(yōu)化實踐

在早期階段,vivo AI 計算平臺使用 GlusterFS 作為底層存儲基座。隨著數(shù)據(jù)規(guī)模的擴大和多種業(yè)務(wù)場景的接入,開始出現(xiàn)性能、維護等問題。為此,vivo 轉(zhuǎn)而采用了自研的軒轅文件系統(tǒng),該系統(tǒng)是基于 JuiceFS 開源版本開發(fā)的一款分布式文件存儲方案。

本文將介紹 vivo 軒轅文件系統(tǒng)在 JuiceFS 基礎(chǔ)之上開發(fā)的新特性。以及 vivo 針對一些關(guān)鍵場景,如樣本數(shù)據(jù)讀取速度慢和檢查點寫入環(huán)節(jié)的優(yōu)化措施。此外,文章還將介紹 vivo 的技術(shù)規(guī)劃包括 FUSE、 元數(shù)據(jù)引擎及 RDMA 通信等方面,希望能為在大規(guī)模 AI 場景使用 JuiceFS 的用戶提供參考與啟發(fā)。01 計算平臺引入軒轅文件存儲的背景

01 計算平臺引入軒轅文件存儲的背景

最初,vivo 的 AI 計算平臺 使用 GlusterFS ,并由該團隊自行維護。在使用過程中,團隊遇到了一些問題。一是處理小文件時速度變得非常緩慢;二是當需要對 GlusterFS 進行機器擴容和數(shù)據(jù)平衡時,對業(yè)務(wù)產(chǎn)生了較大的影響。

隨后,由于早期集群容量已滿且未進行擴容,計算團隊選擇搭建了新的集群。然而,這導(dǎo)致了多個集群需要維護,從而增加了管理的復(fù)雜度。此外,作為平臺方,他們在存儲方面的投入人力有限,因此難以進行新特性開發(fā)。

他們了解到我們互聯(lián)網(wǎng)部門正在研發(fā)文件存儲解決方案,經(jīng)過深入交流和測試。最終,他們決定將其數(shù)據(jù)存儲遷移至我們的軒轅文件存儲系統(tǒng)。

軒轅文件系統(tǒng)基于 JuiceFS 開源版,進行了二次開發(fā),支持多種標準訪問協(xié)議,包括 POSIX、HDFS 以及 Windows 上的 CIFS 協(xié)議。此外,我們還提供了文件恢復(fù)功能,該功能參考了商用解決方案,能夠按照原路徑進行數(shù)據(jù)恢復(fù)。

同時,我們的系統(tǒng)支持客戶端熱升級,這一功能在開源版本中也已經(jīng)實現(xiàn)。另外,我們還支持用戶名權(quán)限管理,默認使用本地 uid/gid 進行鑒權(quán)。在此基礎(chǔ)上,我們還參考 JuiceFS 企業(yè)版實現(xiàn)了用戶名鑒權(quán)功能。

下圖是軒轅文件系統(tǒng)的架構(gòu)圖,與 JuiceFS 類似。在底層基座方面,我們使用 TikV 存儲元數(shù)據(jù),而數(shù)據(jù)則存儲在我們自研的對象存儲系統(tǒng)中。特別值得一提的是,在 Windows 場景下,我們在 Samba 中開發(fā)了一個插件,該插件直接調(diào)用 JuiceFS API,從而為用戶提供了一個在 Windows 上訪問我們文件存儲的通道。

目前的 AI 計算平臺存儲流程如下:首先獲取原始數(shù)據(jù)并通過一個包含 4 萬個批處理任務(wù)的系統(tǒng)進行處理,生成樣本庫。這些樣本庫隨后在 GPU 上訓(xùn)練,產(chǎn)生模型文件,這些模型文件被傳輸至在線系統(tǒng)用于推理。原始數(shù)據(jù)及處理后的樣本庫直接存儲在軒轅文件系統(tǒng)中,由于其兼容 HDFS API,Spark 可以直接處理這些數(shù)據(jù)。模型文件也保存在軒轅中,并通過其提供的CSI插件,使在線推理系統(tǒng)能直接掛載并讀取這些文件。

02 存儲性能優(yōu)化

訓(xùn)練階段涉及存儲的主要有兩個重要方面:樣本讀和訓(xùn)練過程中的檢查點( checkpoint) 保存。

環(huán)節(jié)1:加速樣本讀

為了提升樣本加載的速度,我們開發(fā)了一個分布式讀緩存層。在訓(xùn)練模型前,我們借助JuiceFS 提供的 warm up 功能,優(yōu)先將本次訓(xùn)練所需的數(shù)據(jù)預(yù)加載至讀緩存層。通過這種方式,訓(xùn)練數(shù)據(jù)可以直接從讀緩存層獲取,而無需從對象存儲系統(tǒng)中拉取。通常情況下,直接從對象存儲中讀取數(shù)據(jù)需要花費十幾至幾十毫秒,但通過讀緩存層則可將讀取時間縮短至 10 毫秒以內(nèi),從而進顯著提高了數(shù)據(jù)加載到 GPU的 速度。

環(huán)節(jié)2:檢查點 (Checkpoint) 寫入

在檢查點寫入方面,我們參考了百度的方案。具體而言,檢查點數(shù)據(jù)首先被寫入一個臨時緩存區(qū)域(我們稱之為“協(xié)管”區(qū)域,但此處可能指的是某種形式的中間緩存或暫存區(qū)),然后再逐步刷新到對象存儲中。在這個過程中,我們也采用了單副本模式,因為檢查點本身就是每隔一段時間保存的,即使某個時間段的檢查點丟失,對整體訓(xùn)練的影響也是有限的。當然,我們也制定了一些策略來確保關(guān)鍵數(shù)據(jù)的安全性,并非所有數(shù)據(jù)都會進入這個中間緩存區(qū)域。通常,只有檢查點文件和訓(xùn)練階段的日志文件會被寫入。如果訓(xùn)練中斷,檢查點文件可以從這個中間緩存區(qū)域中讀取。

此外,當數(shù)據(jù)被寫入并刷新到對象存儲中時,我們并不會立即從檢查點緩存中清除這些數(shù)據(jù)。因為訓(xùn)練過程中隨時可能中斷,如果此時檢查點緩存中的數(shù)據(jù)被清除,而需要從對象存儲中重新拉取,將會耗費較長時間。因此,我們設(shè)置了一個 TTL(生存時間)機制。例如,如果檢查點數(shù)據(jù)每小時刷新一次到對象存儲中,我們可以將 TTL 設(shè)置為 1.5 小時。這樣,即使訓(xùn)練中斷,我們也能確保檢查點緩存中有一個最新的備份可供使用。

在開發(fā)寫緩存的過程中,我們遇到了一個挑戰(zhàn)。由于我們的客戶端與寫緩存之間的通信采用 gRPC 協(xié)議,該協(xié)議在數(shù)據(jù)反序列化時會重新申請內(nèi)存以存儲解析后的數(shù)據(jù)。在特定時間段內(nèi),如果寫操作非常集中(例如在幾十秒內(nèi)),會導(dǎo)致大量的內(nèi)存申請和釋放。由于我們使用的是 Go 語言開發(fā),其垃圾回收(GC)機制在這種情況下表現(xiàn)較慢,可能會導(dǎo)致寫緩存的內(nèi)存耗盡。

為了解決這個問題,我們調(diào)研了其他數(shù)據(jù)反序列化的方案。最終,我們采用了 Facebook 的 flatterbuffer 方案。與 gRPC 的 Pb 反序列化不同,flatterbuffer 在反序列化后可以直接使用數(shù)據(jù),無需額外的解析步驟。通過這種方式,我們減少了內(nèi)存的使用,與 Pb 相比,內(nèi)存節(jié)省達到了 50%。同時,我們也對寫性能進行了測試,發(fā)現(xiàn)使用 flatterbuffer 后,寫性能提升了20%

環(huán)節(jié)3:在線推理,模型加載流量大

在用戶進行在線推理時,我們注意到模型下載產(chǎn)生的流量極大,有時甚至?xí)紳M對象存儲網(wǎng)關(guān)的帶寬。深入分析這個場景后,我們發(fā)現(xiàn)存在眾多實例,每個實例都會獨立地將完整模型加載到內(nèi)存中,并且這些實例幾乎是同時開始加載模型的,這一行為造成了巨大的流量壓力。

為解決此問題,我們借鑒了商業(yè)解決方案,采用了在 Pod 中實施邏輯分組的方法。在這種策略下,每個分組僅從底層存儲讀取一份完整模型,而分組內(nèi)的各個節(jié)點則讀取模型的部分文件,并通過節(jié)點間的數(shù)據(jù)共享(類似于 P2P 方式)來減少總體流量需求。這種方法顯著降低了對底層對象存儲帶寬的占用,有效緩解了流量壓力。

03 技術(shù)規(guī)劃

libc 調(diào)用繞過 FUSE 內(nèi)核,提升讀寫性能 下面這份圖表來源于 ACM 期刊中的一篇論文。文中指出,在使用 FUSE 掛載時,請求的處理流程會先從用戶態(tài)轉(zhuǎn)移到內(nèi)核態(tài),然后再返回用戶態(tài)。在這個流程中,上下文切換所帶來的消耗是相當巨大的。

柱狀圖較高的部分代表原生的 FUSE,而柱狀圖較低的部分則代表經(jīng)過優(yōu)化的方案。

  • 小文件場景:原生的 FUSE 相較于優(yōu)化方案,其上下文次數(shù)切換的數(shù)量差距達到了 1000 倍;
  • 大文件場景:原生的 FUSE 與優(yōu)化方案之間的上下文次數(shù)切換的數(shù)量差距約為 100 倍;
  • 混合負載場景:同樣顯示出了巨大的上下文次數(shù)切換的數(shù)量差異。

在論文中提到,鏈路消耗的主要來源是上下文切換。因此,我們計劃在 FUSE 這一層進行優(yōu)化,主要針對元數(shù)據(jù)和小文件場景。目前,我們正在進行方案選型工作。

自研元數(shù)據(jù)引擎,文件語義下沉

我們還計劃開發(fā)一個自己的元數(shù)據(jù)引擎。當前,我們使用的元數(shù)據(jù)引擎是基于 TiKV 的,但 TiKV 并不具備文件語義,所有的文件語義都是在客戶端實現(xiàn)的。這給我們的特性開發(fā)工作帶來了極大的不便。

同時,當多個節(jié)點同時寫入一個 key 時,事務(wù)沖突也會非常頻繁。近期,我們還遇到了進程會突然卡住的問題,持續(xù)時間從幾分鐘到十幾分鐘不等。這個問題一直未能得到解決。

另外,TiKV PD 組件為主節(jié)點 Active 模式,請求上 10 萬后,時延上升明顯,PD 節(jié)點(112核)CPU 使用率接近飽和。因此,我們正在嘗試一些方案來降低主節(jié)點的 CPU 利用率,以觀察是否能改善耗時問題。我們參考了一些論文,如百度的 CFS 論文,將所有的元數(shù)據(jù)操作盡量變成單機事務(wù),以減少分布式事務(wù)的開銷。

緩存層實現(xiàn) RDMA

通信關(guān)于我們機房的 GPU 節(jié)點,它們目前使用的是 RDMA 網(wǎng)絡(luò)。與緩存層的通信仍然使用 TCP 協(xié)議。我們有規(guī)劃開發(fā)一個基于 RDMA 的通信方式,以實現(xiàn)客戶端與緩存之間的低延遲、低 CPU 消耗的通信。

通過觀察客戶端的火焰圖,我們發(fā)現(xiàn) RPC 通信的耗時仍然非常明顯。雖然寫緩存的處理數(shù)據(jù)只需要一兩毫秒,但客戶端將數(shù)據(jù)上傳到整個鏈路的耗時可能達到五六毫秒,甚至十毫秒。在客戶端 CPU 非常繁忙的情況下,這個時間可能會達到二三十毫秒。而 RDMA 本身并不怎么消耗 CPU,內(nèi)存消耗也比較少,因此我們認為這是一個值得嘗試的解決方案。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容