傳統的機器學習模型,資料集比較小,模型的演演算法也比較簡單,使用單機儲存,或者本地硬碟就足夠了,像 JuiceFS 這樣的分散式儲存並不是必需品。
隨著近幾年深度學習的蓬勃發展,越來越多的團隊開始遇到了單機儲存的瓶頸,分散式儲存在 AI 領域的重要性不斷凸顯。AI 團隊通常會面臨以下幾種問題:
資料集太大
隨著資料量和模型規模的增加,單機儲存往往無法滿足需求。為解決這些問題,就需要使用分散式儲存。
歷史資料集需要進行全量歸檔
在某些應用場景每天都會產生大量新的資料集,這些資料集在一段時間後將變為歷史資料,需要進行歸檔。由於這些資料的生成成本較高,因此不能輕易刪除,尤其是在自動駕駛領域,如路測車採集的雷達和攝像頭資料,這些資料對公司來說是極為寶貴的資產。若要整理這些資料,傳統的單機儲存顯然不足以滿足需求,因此需要考慮使用分散式儲存。
小檔案和非結構化資料太多
針對傳統的分散式檔案系統,管理大量小檔案會帶來後設資料儲存的負擔,對於視覺類模型,影響更加明顯。解決這一問題的方法是使用對小檔案儲存友好的分散式儲存系統,這樣可以保證上層訓練任務的高效率,同時也能夠方便地管理大量的小檔案。
訓練框架需要 POSIX 介面
最初演演算法科學家在做模型調研時,都是基於原生的資源來做研發和資料的存取,但當需要在分散式儲存上進行更大規模的訓練時,原本的程式碼一般不會做太多的調整。所以這就要求分散式儲存需要支援 POSIX 介面,最大程度上相容本地開發階段的程式碼。
公共資料集需要不同團隊共用,也可能需要資料隔離
在某些領域,如計算機視覺,有一些權威的公共資料集,這些資料集需要在公司內部不同的團隊間共用。為了方便團隊之間的使用,通常會將這些資料整合並打包儲存到一個共用儲存中,避免不必要的資料複製和冗餘。
雲上訓練的資料 I/O 效率不高
在雲上進行模型訓練通常是使用物件儲存作為底層儲存的存算分離架構,由於物件儲存的讀寫效能較差,在訓練上會有很大的瓶頸。
本文將會介紹在模型訓練中如何使用 JuiceFS,以及優化訓練效率的實踐。
上圖是架構圖,分為三個部分:
第一部分:後設資料引擎,根據個人選擇,可以使用任何資料庫,例如 Redis、MySQL 等等,作為後設資料引擎。
第二部分:底層資料儲存,在雲上或私有云中,使用物件儲存服務來對接 JuiceFS。
第三部分:JuiceFS 使用者端,使用者在使用時需要在每個 GPU 和計算節點上掛載 JuiceFS,這樣就可以像存取本地硬碟一樣存取 JuiceFS 的檔案系統。
底層儲存依賴於物件儲存中的原始資料,同時每個計算節點上還有一些本地快取,包括後設資料和資料快取。JuiceFS 的設計中,每個計算節點的本地可以有多級快取。第一級是基於記憶體的快取,第二級是基於本地磁碟的快取,只有在本地快取沒有命中時,才會存取物件儲存。
如果進行單機模型訓練,在首輪訓練時,訓練集或資料集通常不會命中快取。但是從第二輪開始,在快取資源充足的情況下,幾乎不需要存取物件儲存,達到加速資料 I/O 的效果。
我們之前進行了一項評測,比較了在存取物件儲存時,使用快取和不使用快取這兩種方式對於訓練效率的影響。評測結果表明,這兩種方式的效能差別非常大。(點選 此處,瞭解評測結果)
上圖展示了 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 效能影響比較小,但是在大量小檔案的場景,如果需要頻繁存取小檔案,後設資料的開銷也會佔到一定的比重。
當使用 JuiceFS 進行訓練時,效能是最重要的考慮因素,它直接影響到模型訓練的速度。以下是可能影響 JuiceFS 效率的幾個方面:
後設資料引擎
在處理小檔案時,選擇不同的後設資料引擎(如 Redis、TiKV、MySQL)的效能差別很大。JuiceFS 官網提供了一份對比它們作為後設資料引擎的效能檔案,平均來說 Redis 會比其他資料庫快 3~5 倍。如果發現後設資料請求特別慢,建議嘗試使用一些效能更好的資料庫作為 JuiceFS 的後設資料引擎。
物件儲存
主要影響資料儲存存取的效能和吞吐量。如果在雲上使用,通常使用公有云提供的物件儲存服務,其效能相對固定。如果使用自建的物件儲存,例如使用開源的 Ceph 或 MinIO 元件,可以對元件進行調優以達到更好的效能和吞吐量。
本地磁碟
快取目錄儲存的位置對整個讀取效能影響很大。在快取命中率高的情況下,快取磁碟的 I/O 效率會直接影響整體 I/O 效率。因此需要注意儲存型別、儲存媒介以及磁碟容量等因素,資料集的大小也會對訓練效率產生影響。
網路頻寬
在第一輪訓練完成後,如果資料集不足以在本地完全快取,網路頻寬或網路資源的消耗會影響整體資料存取效率。在雲上,不同機型的網路卡頻寬也有所不同,這也會對資料的存取速度和效率產生影響。
記憶體
記憶體的大小會直接影響核心頁快取的大小。當記憶體足夠大時,剩餘的空閒記憶體可以用作 JuiceFS 資料的快取,進一步加快資料的存取速度。但是,當剩餘的空閒記憶體較少時,資料存取需要通過本地磁碟獲取,這會導致存取開銷變大。另外,核心態和使用者態之間的切換會對效能造成影響,比如系統呼叫的上下文切換開銷等。
JuiceFS 提供了許多工具和命令來幫助使用者更好地進行效能調優和診斷。在去年的 Office Hours 中,已經對如何在 JuiceFS 中進行效能調優和診斷進行了全面介紹。如果感興趣,可以在 B 站上觀看視訊回放。以下是其中幾個方法的簡要介紹:
工具1 :juicefs profile 命令
它可以通過分析存取紀錄檔來幫助使用者更好地優化效能。每個檔案系統掛載之後都會有存取紀錄檔,但存取紀錄檔並不會實時儲存,只有在檢視存取紀錄檔時才會顯示出來。相比直接檢視原始的存取紀錄檔,juicefs profile 命令會進行資訊的聚合和類似滑動視窗的資料統計,並按照響應時間從高到低排序,幫助使用者優先關注響應時間較慢的請求,進一步分析請求與後設資料引擎或物件儲存的關係。
工具2:juicefs stats 命令
它從更宏觀的角度收集監控資料,並實時展示出來。它可以監控當前掛載點的 CPU 佔用、記憶體佔用、記憶體中的緩衝區佔用、FUSE 讀寫請求、後設資料請求以及物件儲存的延遲情況等。這些細緻的監控指標可以方便使用者檢視和分析當前模型訓練的瓶頸或效能問題出現的可能環節。
JuiceFS 還提供了更底層的資訊分析工具,包括 CPU profile 和 heap profile。CPU profile 可以分析 JuiceFS 程序執行速度的瓶頸所在,適用於熟悉原始碼的使用者。而 heap profile 則主要用於分析記憶體佔用情況,尤其是當 JuiceFS 程序佔用大量記憶體時,需要使用 heap profile 來確定具體哪些函數或資料結構佔用了較多記憶體。
後設資料快取的優化方案主要分為兩類:
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 命令實時觀測緩衝區的使用量。
如有幫助的話歡迎關注我們專案 Juicedata/JuiceFS 喲! (0ᴗ0✿)