基礎設施

以數週的常駐 VRAM 資料評估本地 GPU 容量

規格表告訴您的是顯示卡的記憶體,而非您的容器在靜止時實際佔用的記憶體。以下說明數週的常駐 VRAM 讀數如何為單一地端 GPU 進行規模估算。

本文由 AI 模型從英文原文翻譯而來,用字可能與原文有所出入。 閱讀英文原文

當您將機器學習推論工作負載從雲端移至單一地端機器時,第一個問題就是單一 GPU 是否足夠。規格表的答案在這裡毫無用處。決定因素是您的容器在閒置時實際佔用的記憶體,以及在負載下達到的峰值。我透過讀取三張已執行相同映像檔數週的雲端 GPU 的常駐 VRAM,來評估地端 GPU 的大小,而這些數字讓決定變得顯而易見。

常駐記憶體是基準,而非規格表

一個在啟動時載入其模型的推論容器,在不同任務之間並不會釋放該記憶體。它會將整個模型集保持在常駐狀態,這樣下一個請求就不需要付出冷載入的代價。因此,設定您基準的數字不是顯示卡的大小,也不是請求期間的峰值。而是容器閒置時穩定的常駐記憶體佔用量。

有三樣東西會疊加到該佔用量中。在從磁碟讀取任何一個權重之前,每個程序的 CUDA 上下文就會耗費數百 MiB。接下來是模型權重,對於一個提供多個模型的影像服務來說,權重是佔用量的大宗。然後是配置器:像 PyTorch 這類的框架會快取從驅動程式取得的大區塊,因此一個被釋放的張量會回到該快取,而不是回到驅動程式。從外部看,容器似乎從不歸還記憶體,因為它確實不會。

這就是為什麼常駐集會攀升一段時間然後達到平穩,而不是從開機後就保持不變。每一個新的請求形狀,例如更長的輸入或更大的批次,都可能迫使配置器劃分一個它尚未持有的區塊,而該區塊會被保留下來。在經過數天混合流量的處理後,常駐集會穩定在該程序曾服務過的每種形狀的最高水位標記附近。一個在開機五分鐘後測量的容器尚未遇到那些形狀,所以讀數會偏低,而您會低估需求。

應按程序測量,而非按顯示卡。全裝置的使用數據會混入所有其他程序,包括您即將移除的桌面工作階段。

# Per-process resident VRAM. This is the number that sizes the card.
nvidia-smi --query-compute-apps=pid,used_gpu_memory --format=csv
# Device totals, for the headroom check.
nvidia-smi --query-gpu=memory.total,memory.used,memory.free --format=csv

報告的總量,就是規格表與事實不符之處。一張標示為 24 GB 的顯示卡並不會提供 24 GB 的可定址記憶體:驅動程式會保留一部分,而在啟用 ECC 的組件上,校正位元會佔用更多空間。兩張同屬 24 GB 等級的顯示卡,其報告的總量可能相差達一 gigabyte,而當你的記憶體餘裕只有幾個 gigabyte 時,這點就很重要了。

讀數顯示的結果

雲端中有三張 GPU 正在執行相同的映像檔,每張都是 23 GB 的顯示卡。我在三張卡閒置時讀取了其推論容器的常駐 VRAM:

GPU 常駐 VRAM (閒置) 備註
A 15,322 MiB 單一長期執行程序,執行時間超過 6 週
B 17,382 MiB 觀測到的最高值
C 13,676 MiB 觀測到的最低值

在相同的映像檔之間,從 13.7 到 17.4 GB 的差距就是碎片化效應。這三個程序之間的權重沒有任何不同,所以整個 3,706 MiB 的差距都是配置器歷史記錄造成的:也就是每個實例碰巧服務了哪些請求形狀,以及服務的順序。最高的讀數比最低的高出 27%。

這就是為什麼平均值是個陷阱。三者的平均值是 15,460 MiB,比 GPU B 實際佔用的少了 1.9 GB。在一張有六或七 GB 餘裕的顯示卡上,根據平均值來規劃大小,在第一個請求到達前就消耗了三分之一的邊際空間。

比任何常駐數字更重要的是那些沒有發生的事。在數週的生產環境中,基於該常駐集合的峰值推論都維持在 23 GB 顯示卡內:沒有記憶體不足的終止,沒有配置器重試風暴,也沒有重新啟動。這一個事實涵蓋了真實使用者發送的每種請求形狀,包括沒有人會放進基準測試中的長尾部分。

無頭模式開機,討回被桌面環境竊佔的資源

那台本地部署的機器是一台工作站,它會開機進入完整的桌面環境。在推論容器啟動前,顯示管理器及其合成器就佔用了數百 MiB 的 GPU 記憶體。對於一台沒有人會登入的工作站來說,這純粹是浪費,而且正好佔用了您正在嘗試估算大小的資源。

數百 MiB 對比一張 24 GB 的顯示卡聽起來像是捨入誤差,而相對於整張卡的容量來說,它的確是。但相對於餘裕空間來說,就不是了。如果容器在最壞情況下佔用 17.4 GB,那麼您能管理的餘裕就接近 6.6 GB,而 500 MiB 的登入畫面就佔了其中的百分之八。衡量浪費時,應以餘裕為基準,而非總容量。

# Boot to a console, and stop the greeter from claiming VRAM.
sudo systemctl set-default multi-user.target
sudo systemctl disable --now gdm3   # or lightdm, sddm

切換後,GPU 程序減少到只剩下推論容器。請驗證而非假設:重新啟動並確認 compute-apps 查詢列出一個項目。請在機器實際運行的狀態下進行每一次測量,因為在桌面環境啟動的情況下測量出的數字,描述的是一台不存在的機器。

規格大小的決定

地端顯示卡是一張 24 GB 的 GPU,比那些已經證明足以應付該工作負載的 23 GB 雲端顯示卡大了約 1.5 GB。計算方式如下:觀測到最差的常駐記憶體為 17,382 MiB,而雲端顯示卡在運行該容器數週時,記憶體用量之上仍有約 5.6 GB 的餘裕,且從未耗盡顯示卡。地端顯示卡回報的容量多了 1.5 GB,因此在同樣的最差情況下,同一個映像檔會有約 7.1 GB 的餘裕,而且這是在一台桌面環境不再佔用資源的機器上。

這是一個優勢論證,而非預測。我從未測量過推論的峰值,也無須這麼做。只要新的顯示卡嚴格來說大於那張已經撐過這個工作負載、這個映像檔以及數週真實流量的卡,就足夠了。任何之前能容納的峰值,現在也能容納,而且還多出 1.5 GB 的空間。

成本的不對稱性讓這個決定變得很簡單。第二張 GPU 不僅僅是一張卡的價格:它需要一個空閒的插槽、電源供應器的餘裕以及散熱能力,而機殼可能這三者都沒有,再加上還得費工去切分一個假設只有單一裝置的服務路徑。反之,若記憶體不足,則會導致生產環境停擺。有了這個優勢論證,這兩種風險都不需要被估算成本。一張卡就夠了,而數週的雲端數據也證實了這一點,其信心度比我一個下午能跑完的任何基準測試都還要高。

購買前該測量什麼

此通用方法適用於任何駐留密集型的推論工作負載:

  • 在一個已經服務真實流量數天(而非剛開機)的容器上,讀取每個程序的駐留 VRAM。取相同執行個體中的最大值,並將其視為下限而非上限。
  • 確認在生產環境中,駐留記憶體加上推論峰值仍在已知運作良好的卡容量範圍內。如果您要購買的卡所報告的總量大於那張已驗證的卡,那麼尺寸就確定了。
  • 比較報告的 memory.total,而非行銷宣傳的容量。同一級距的兩張卡可能相差一 GB。
  • 如果機器是工作站,請在關閉顯示管理器的情況下測量,這樣桌面環境才不會佔用您預計要使用的預留空間。
  • 購買容量能應付您最壞情況下的駐留記憶體加上峰值,且仍有額外空間的卡,然後就此打住。您不需要的 GPU 是機架中最昂貴的閒置硬體。

此方法基於一個假設:工作負載是駐留密集型的,且其記憶體足跡是穩定的。訓練會打破這個假設,因為啟用值和優化器狀態會隨批次大小和序列長度擴展,因此峰值遠大於閒置基底,必須直接估算其大小。依請求載入和卸載模型的服務則從另一方面打破了這個假設,其駐留集很小,而峰值就是全部。模型集不斷增長的多租戶服務也會打破它,因為本季的最壞情況不是下一季的。對映像檔的任何變更也是如此:新模型或框架升級會改變基底,並使您的讀數失效。

常見問題

我應該根據平均還是最大的常駐讀數來調整大小?

根據最大值,然後在其上保留餘裕。相同執行個體之間的差異來自配置器歷史,因此最高的讀數屬於服務過最廣泛請求形狀組合的那個執行個體。僅有少數幾個執行個體,您只是對該分佈進行了取樣,而不是界定了其範圍。

如果請求形狀改變,常駐記憶體下限會移動嗎?

會。這個下限會追蹤程序服務過的每種配置形狀的最高水位線,因此提高最大批次大小或上下文視窗會在其生命週期內推高這個下限。在調整大小後更改服務參數,您舊的讀數描述的是一個您不再運行的容器。

我可以限制記憶體用量,而不是購買更大的顯示卡嗎?

部分可以。調整配置器可以減少碎片化並限制程序快取的數量,這會降低常駐集,但它無法縮小權重,而強制將區塊送回驅動程式是以吞吐量換取餘裕空間。如果差距是幾百 MiB,這種交換通常是值得的。如果是幾 GB,您就需要一張更大的顯示卡或一套更小的模型。

如果我在一個 GPU 上運行兩個推論容器會發生什麼?

它們的常駐集會相加,不會重疊。每個程序都會支付自己幾百 MiB 的 CUDA 上下文,並保留自己的權重副本,因此兩個 15 GB 映像檔的容器需要 30 GB 而不是 15 GB。將多個程序打包到一張顯示卡上只有在每個模型都很小,且顯示卡遠大於它們下限總和時才划算。