傳統的機器學習模型,數據集比較小,模型的算法也比較簡單,使用單機存儲,或者本地硬盤就足夠了,像JuiceFS 這樣的分布式存儲并不是必需品。
隨著近幾年深度學習的蓬勃發展,越來越多的團隊開始遇到了單機存儲的瓶頸,分布式存儲在 AI 領域的重要性不斷凸顯。AI 團隊通常會面臨以下幾種問題:
數據集太大
隨著數據量和模型規模的增加,單機存儲往往無法滿足需求。為解決這些問題,就需要使用分布式存儲。
歷史數據集需要進行全量歸檔
在某些應用場景每天都會產生大量新的數據集,這些數據集在一段時間后將變為歷史數據,需要進行歸檔。由于這些數據的生成成本較高,因此不能輕易刪除,尤其是在自動駕駛領域,如路測車采集的雷達和攝像頭數據,這些數據對公司來說是極為寶貴的資產。若要整理這些數據,傳統的單機存儲顯然不足以滿足需求,因此需要考慮使用分布式存儲。
小文件和非結構化數據太多
針對傳統的分布式文件系統,管理大量小文件會帶來元數據存儲的負擔,對于視覺類模型,影響更加明顯。解決這一問題的方法是使用對小文件存儲友好的分布式存儲系統,這樣可以保證上層訓練任務的高效率,同時也能夠方便地管理大量的小文件。
訓練框架需要 POSIX 接口
最初算法科學家在做模型調研時,都是基于本地的資源來做研發和數據的訪問,但當需要在分布式存儲上進行更大規模的訓練時,原本的代碼一般不會做太多的調整。所以這就要求分布式存儲需要支持 POSIX 接口,最大程度上兼容本地開發階段的代碼。
公共數據集需要不同團隊共享,也可能需要數據隔離
在某些領域,如計算機視覺,有一些權威的公共數據集,這些數據集需要在公司內部不同的團隊間共享。為了方便團隊之間的使用,通常會將這些數據整合并打包存儲到一個共享存儲中,避免不必要的數據復制和冗余。
云上訓練的數據 I/O 效率不高
在云上進行模型訓練通常是使用對象存儲作為底層存儲的存算分離架構,由于對象存儲的讀寫性能較差,在訓練上會有很大的瓶頸。
本文將會介紹在模型訓練中如何使用 JuiceFS,以及優化訓練效率的實踐。
1.JuiceFS 在模型訓練場景中的架構
上圖是架構圖,分為三個部分:
第一部分:元數據引擎,根據個人選擇,可以使用任何數據庫,例如 Redis、MySQL 等等,作為元數據引擎。
第二部分:底層數據存儲,在云上或私有云中,使用對象存儲服務來對接 JuiceFS。
第三部分:JuiceFS 客戶端,用戶在使用時需要在每個 GPU 和計算節點上掛載 JuiceFS,這樣就可以像訪問本地硬盤一樣訪問 JuiceFS 的文件系統。
底層存儲依賴于對象存儲中的原始數據,同時每個計算節點上還有一些本地緩存,包括元數據和數據緩存。JuiceFS 的設計中,每個計算節點的本地可以有多級緩存。第一級是基于內存的緩存,第二級是基于本地磁盤的緩存,只有在本地緩存沒有命中時,才會訪問對象存儲。
如果進行單機模型訓練,在首輪訓練時,訓練集或數據集通常不會命中緩存。但是從第二輪開始,在緩存資源充足的情況下,幾乎不需要訪問對象存儲,達到加速數據 I/O 的效果。
JuiceFS 讀寫緩存流程
我們之前進行了一項評測,比較了在訪問對象存儲時,使用緩存和不使用緩存這兩種方式對于訓練效率的影響。評測結果表明,這兩種方式的性能差別非常大。(點擊,了解評測結果)
上圖展示了 JuiceFS 緩存讀寫的流程。最上面是應用程序,它相當于是最初發起讀請求的起點應用或訓練任務。
當應用程序發起讀請求后,請求會先進入左側的內核空間,內核會查看內核頁緩存中是否有請求的數據。如果內核頁緩存中沒有數據,請求會回到用戶空間的 JuiceFS 進程。在用戶空間,JuiceFS 進程會處理所有的讀寫請求。
JuiceFS 默認會在內存中維護一個讀緩沖區,當請求未能從緩沖區中獲取數據時,JuiceFS 會進入塊緩存索引,即基于本地磁盤的緩存目錄。JuiceFS 默認將文件切割成 4MB 的塊并存儲,因此緩存的粒度也是 4MB。
舉個例子,當訪問一個文件的一部分數據時,JuiceFS 只會緩存該部分數據對應的 4MB 塊到本地緩存目錄中,而不會緩存整個文件。這是 JuiceFS 與其他文件系統或緩存系統的顯著差異之一。
Block cache index 用于快速定位本地緩存目錄中的文件塊。如果找到了,JuiceFS 進程會自動讀取本地盤,然后進入內核態,讀取完成后返回給 JuiceFS 進程,最后返回給應用程序。
當本地盤數據讀取完成后,數據還會進入內核頁緩存。這是因為如果沒有使用 direct I/O,Linux 系統默認會將數據存儲在內核頁緩存中。這些內核頁緩存都用于加速緩存訪問,如果第一個請求直接命中并返回,那么效率是最高的,并且請求不會通過 FUSE 層進入用戶態進程。如果沒有命中,則會通過 index 查找,如果在節點目錄中沒有找到 block,則會通過網絡請求到達對象存儲,然后將數據讀回來并原路返回給應用程序。
從對象存儲上下載數據時,JuiceFS 會有一個后臺異步線程,把讀回來的 block 同時寫到本地緩存盤里,確保下一次訪問同樣的 block 時,能直接從本地緩存命中,而不需要再次從對象存儲上獲取。以上就是 JuiceFS 建立讀緩存的流程。
上圖有一部分的模塊叫 Chunk Cache,chunk 是 JuiceFS 中的一個邏輯概念,每個文件會按照 64MB 大小分為多個 chunk,來提升大文件的讀取性能。這部分信息會被緩存到 JuiceFS 進程的內存里,來加速元數據訪問的效率。
與數據緩存不同,元數據緩存時間較短,并且為確保強一致性,open 操作默認不緩存。考慮到元數據流量很小,所以對整體的 I/O 性能影響比較小,但是在大量小文件的場景,如果需要頻繁訪問小文件,元數據的開銷也會占到一定的比重。
2.為什么訓練太慢以及如何排查?
當使用 JuiceFS 進行訓練時,性能是最重要的考慮因素,它直接影響到模型訓練的速度。以下是可能影響 JuiceFS 效率的幾個方面:
元數據引擎
在處理小文件時,選擇不同的元數據引擎(如 Redis、TiKV、MySQL)的性能差別很大。JuiceFS 官網提供了一份對比它們作為元數據引擎的性能文檔,平均來說 Redis 會比其他數據庫快 3~5 倍。如果發現元數據請求特別慢,建議嘗試使用一些性能更好的數據庫作為 JuiceFS 的元數據引擎。
對象存儲
主要影響數據存儲訪問的性能和吞吐量。如果在云上使用,通常使用公有云提供的對象存儲服務,其性能相對固定。如果使用自建的對象存儲,例如使用開源的 Ceph 或 MinIO 組件,可以對組件進行調優以達到更好的性能和吞吐量。
本地磁盤
緩存目錄存儲的位置對整個讀取性能影響很大。在緩存命中率高的情況下,緩存磁盤的 I/O 效率會直接影響整體 I/O 效率。因此需要注意存儲類型、存儲介質以及磁盤容量等因素,數據集的大小也會對訓練效率產生影響。
網絡帶寬
在第一輪訓練完成后,如果數據集不足以在本地完全緩存,網絡帶寬或網絡資源的消耗會影響整體數據訪問效率。在云上,不同機型的網卡帶寬也有所不同,這也會對數據的訪問速度和效率產生影響。
內存
內存的大小會直接影響內核頁緩存的大小。當內存足夠大時,剩余的空閑內存可以用作 JuiceFS 數據的緩存,進一步加快數據的訪問速度。但是,當剩余的空閑內存較少時,數據訪問需要通過本地磁盤獲取,這會導致訪問開銷變大。另外,內核態和用戶態之間的切換會對性能造成影響,比如系統調用的上下文切換開銷等。
如何排查
JuiceFS 提供了許多工具和命令來幫助用戶更好地進行性能調優和診斷。在去年的 Office Hours 中,已經對如何在 JuiceFS 中進行性能調優和診斷[1]進行了全面介紹。如果感興趣,可以在 B 站上觀看視頻回放。以下是其中幾個方法的簡要介紹:
工具1 :juicefs profile 命令
它可以通過分析訪問日志來幫助用戶更好地優化性能。每個文件系統掛載之后都會有訪問日志,但訪問日志并不會實時保存,只有在查看訪問日志時才會顯示出來。相比直接查看原始的訪問日志,juicefs profile 命令會進行信息的聚合和類似滑動窗口的數據統計,并按照響應時間從高到低排序,幫助用戶優先關注響應時間較慢的請求,進一步分析請求與元數據引擎或對象存儲的關系。
工具2:juicefs stats 命令
它從更宏觀的角度收集監控數據,并實時展示出來。它可以監控當前掛載點的 CPU 占用、內存占用、內存中的緩沖區占用、FUSE 讀寫請求、元數據請求以及對象存儲的延遲情況等。這些細致的監控指標可以方便用戶查看和分析當前模型訓練的瓶頸或性能問題出現的可能環節。
JuiceFS 還提供了更底層的信息分析工具,包括 CPU profile 和 heap profile。CPU profile 可以分析 JuiceFS 進程執行速度的瓶頸所在,適用于熟悉源代碼的用戶。而 heap profile 則主要用于分析內存占用情況,尤其是當 JuiceFS 進程占用大量內存時,需要使用 heap profile 來確定具體哪些函數或數據結構占用了較多內存。
3.一些常見的優化策略元數據緩存優化
元數據緩存的優化方案主要分為兩類:
1)調整內核元數據緩存的超時時間
可以使用--attr-cache、--entry-cache和--dir-entry-cache參數,這三個參數分別對應不同類型的元數據:attr 表示文件屬性(如大小、修改時間、訪問時間等),entry 是 Linux 中的概念,表示文件和相關屬性,dir-entry 表示目錄和其中包含的文件。
這些參數分別控制著元數據緩存的超時時間。為了保證數據訪問的一致性,這些參數的默認值只有一秒鐘,但在模型訓練的場景中,原始數據通常不會被修改,因此可以將這些參數的超時時間設置得更長一些,比如幾天到一周等。但需要注意的是,元數據緩存是無法主動失效的,只能等待超時時間到期。
2)優化 JuiceFS 客戶端的用戶態元數據緩存
默認情況下,在打開文件時會強制請求元數據引擎獲取最新的文件屬性,以保證強一致性。但由于模型訓練的數據通常不會被修改,因此可以打開--open-cache參數,并設置一個超時時間,以避免每次打開同一個文件都重復訪問元數據引擎。另外可以通過--open-cache-limit參數控制緩存的最大文件數,默認值是 10000,即最多緩存最近打開的 10000 個文件的元數據在內存中,可以根據數據集的文件個數進行適當調整。
數據緩存
JuiceFS 的數據緩存分為內核頁緩存和本地數據緩存兩種,其中內核頁緩存無法進行參數調優。因此,在計算節點上應該盡量保留足夠的空閑內存,以便 JuiceFS 能夠充分利用。如果計算節點上的資源緊張, JuiceFS 就不會將數據緩存到內核中。
而本地數據緩存相對來說用戶更加可控,可以根據具體場景調優緩存參數。首先,可以調整緩存的大小(--cache-size),默認值為 100G,對于大部分場景都足夠了。但是對于占用空間特別大的數據集,需要適當調整緩存大小,否則 100G 的緩存空間可能很快被寫滿,導致 JuiceFS 無法緩存更多數據。配合--cache-size參數一起使用的另一個參數是--free-space-ratio,這個參數用于控制緩存盤的空間空閑比例,默認值是 0.1,即最多使用 90% 的磁盤空間緩存數據。
JuiceFS還支持同時使用多個緩存盤,推薦盡量使用所有可用的盤。數據會通過輪詢的方式均勻分布到多個盤中,從而實現負載均衡,同時最大化利用多塊盤的存儲優勢。
緩存預熱
為了提高訓練效率,可以通過預熱緩存來加速訓練任務。JuiceFS 支持預熱客戶端中的元數據緩存和本地數據緩存,通過使用 juicefs warmup 命令可以將緩存提前預熱到緩存盤,從而在訓練任務開始時直接命中緩存,提高效率。
增大緩沖區大小
緩沖區的大小也會影響讀取性能。默認情況下,緩沖區大小為 300MB,但在高吞吐的訓練場景下,這可能不夠用。可以根據訓練節點的內存資源情況來調整緩沖區大小,一般來說,緩沖區越大讀取性能越好,但也不要設置過大的值(特別是在限制了最大內存的容器環境中)。需要結合實際負載情況進行調優,找到一個相對合理的緩沖區大小。可以參考前面介紹的 juicefs stats 命令實時觀測緩沖區的使用量。
關于作者
高昌健
技術專家,參與建設 JuiceFS 開源社區的主力隊員
引用鏈接
[1]如何在 JuiceFS 中進行性能調優和診斷:
歡迎掃碼加入 JuiceFS 用戶社群
合伙人兼社群助手
蘇銳全天在線
用戶案例
最佳實踐
關于
,杭州果汁數據科技有限公司是一家企業級存儲服務供應商,開發了云原生分布式文件系統 JuiceFS,致力于在大數據時代下,為客戶打造安全、高性能、自主可控的存儲基礎設施及服務
2021年,JuiceFS 正式在 GitHub 上開源,已經獲得 7.9K star,歡迎開發者加入我們。(//juicefs)
訪問官網
*請認真填寫需求信息,我們會在24小時內與您取得聯系。