將推論冷啟動時間縮短 40 倍:真正無伺服器 GPU 背後的工程技術
在當前的 AI 時代,推論需求呈現極度變化的特性。與可預測且穩定的訓練工作負載不同,推論受外部使用者行為驅動,導致需求呈現尖峰模式。對工程師而言,這在服務品質 (QoS) 與成本之間產生了矛盾:過度配置 GPU 以應付高峰會導致可悲的「GPU 分配利用率」下降,而配置不足則會造成延遲尖峰與 503 錯誤。
要實現「真正無伺服器」的 GPU,從請求到執行副本的擴展時間必須從分鐘縮短至秒。Modal 實作了一套四項關鍵優化——雲端緩衝區、客製化懶惰檔案系統(FUSE)、CPU 快照/還原,以及 CUDA 快照/還原——將冷啟動時間縮短至最高 40 倍(從約 2,000 秒降至約 50 秒)。
1. 從熱路徑中移除實例分配
擴展的第一個瓶頸是啟動新虛擬機器並執行健康檢查所需的時間,可能需要數分鐘。Modal 透過維持一個 cloud buffer(雲端緩衝區),即共享於多個應用程式的空閒、健康 GPU,將此步驟從關鍵路徑中移除。
透過將新副本排程至這些預熱的單元,並以非同步方式補充緩衝區,系統消除了最初的分配延遲。此過程以線性規劃問題處理,使用 Google 的 GLOP 求解器,平衡成本、請求容量與觀測到的雲端供應商供給。
關鍵在於此方法包含積極的 GPU 健康檢查。由於 GPU 的失效頻率高於標準伺服器硬體,Modal 實作了兩層健康檢查:開機時的短暫主動檢查,以及每週一次的較密集診斷(如 dcgmi diag)。
2. 透過 ImageFS 實現懶惰容器載入
標準容器啟動的瓶頸在於需要拉取並解壓數 GB 的根檔案系統資料。Modal 透過將容器啟動器與映像傳遞分離,使用以 libfuse 建置的自訂檔案系統 ImageFS 來解決此問題。
懶惰載入與內容位址化
ImageFS 並非載入整個映像,而是在啟動時僅載入中繼資料(索引),耗時不到 100 毫秒。實際的檔案內容則在應用程式請求時才懶惰載入。由於大多數容器從未存取其檔案系統的大部分內容(例如語系或時區資料),因此大量映像實際上根本不會被傳輸。
為了最佳化被存取的資料,Modal 使用 分層、內容位址化快取:
- Page Cache(頁面快取): 微秒級延遲,針對最頻繁的命中。
- Local SSD(本機 SSD): 高吞吐量儲存,用於常用內容。
- Regional CDN/Blob Storage(區域 CDN/Blob 儲存): 無限容量,處理長尾資料。
透過使用內容位址化而非基於路徑或層級的快取,Modal 確保不同映像之間共享的位元組僅儲存一次,無論它們位於哪一層。
3. 使用 CPU 快照快進主機啟動
即使容器已啟動,應用程式仍需初始化。在以 Python 為主的 AI 堆疊中,簡單的 import torch 可能觸發數千次系統呼叫,並產生數秒的開銷。
Modal 利用 Checkpoint/Restore (C/R) 來繞過此步驟。透過使用 gVisor 的 runsc 執行環境,Modal 將容器視為狀態機。它們會建立一個程序的記憶體快照——包括堆、執行緒狀態與檔案描述子表——並將其儲存至磁碟。
當需要新副本時,系統會直接從此快照還原程序至記憶體。此舉「快進」應用程式至就緒狀態,將主機端啟動時間大幅縮短約 10 倍。然而,快照必須與底層 CPU 指令集相容(例如避免使用特定 AWS 實例類型不支援的指令)。
4. 以 CUDA 快照消除裝置初始化
最後且通常最重要的瓶頸是 GPU 端的初始化。這包括兩項主要任務:
- Weight Loading(權重載入): 將數十億參數從儲存體搬移至 GPU VRAM。
- Inference Engine Setup(推論引擎設定): 計算密集的工作,如捕捉 CUDA 圖或執行 Torch 編譯器。
雖然權重載入主要是吞吐量瓶頸(受限於網路/磁碟速度),但引擎設定則是計算瓶頸。Modal 利用近期 Nvidia 驅動程式的功能,將 device memory(裝置記憶體) 快照至主機記憶體。
透過結合主機端與裝置端的快照,Modal 能還原完整的 CUDA 上下文。對於如 vLLM 或 SGLang 等 LLM 伺服器,這大幅降低了啟動延遲。例如,在使用 1 GiB 模型的測試中,啟用快照後 vLLM 的啟動延遲從平均約 95 秒降至約 13 秒。
延遲縮減摘要
| 最佳化 | 目標元件 | 延遲影響 |
|---|---|---|
| Cloud Buffers | 機器管理 | 分鐘 $\rightarrow$ 秒 |
| ImageFS (FUSE) | 本機 SSD / 網路 | 分鐘 $\rightarrow$ 秒 |
| CPU Snapshots | CPU / 記憶體 | 數十秒 $\rightarrow$ 秒 |
| CUDA Snapshots | GPU / VRAM | 分鐘 $\rightarrow$ 數十秒 |
真實案例應用:Reducto
這些最佳化使得高峰值與平均值比率極高的工作負載能有效擴展。Reducto 是一個文件處理平台,使用視覺語言模型處理龐大的企業資料集。他們的工作負載需要在短時間內擴展至數千顆 GPU,以符合緊迫的期限。透過使用 GPU 記憶體快照,Reducto 將冷啟動時間從約 70 秒縮減至約 12 秒,使其能以真正無伺服器的方式運行「千 GPU」工作負載,且無需維持昂貴的閒置容量。
技術反思與考量
雖然 Modal 的方法非常有效,但社群指出了幾項技術取捨:
- FUSE Overhead(FUSE 開銷): 使用
libfuse會在使用者與核心空間之間產生額外的上下文切換。雖然對於吞吐量密集的 AI 工作負載影響可忽略不計,但對於延遲敏感的檔案操作可能成為瓶頸。 - Snapshot Fragility(快照脆弱性): 記憶體快照對主機環境極為敏感。若在具備特定 CPU 指令的機器上建立快照,則無法在缺少該指令的機器上還原,這需要為異質叢集準備多個快照。
- Multi-GPU Complexity(多 GPU 複雜性): 為多 GPU 程式做快照具有挑戰性,因為像
nccl之類的通信庫並未設計為可暫停,且在還原過程中可能發生死結。