一、GPU 利用率的定義
本文的 GPU 利用率主要指 GPU 在時間片上的利用率,即通過 nvidia-smi 顯示的 GPU-util 這個指標。統(tǒng)計方式為:在采樣周期內,GPU 上面有 kernel 執(zhí)行的時間百分比。
二、GPU 利用率低的本質
常見 GPU 任務運行流程圖如下:
如上圖所示,GPU 任務會交替的使用 CPU 和 GPU 進行計算,當 CPU 計算成為瓶頸時,就會出現(xiàn) GPU 等待的問題,GPU 空跑那利用率就低了。那么優(yōu)化的方向就是縮短一切使用 CPU 計算環(huán)節(jié)的耗時,減少 CPU 計算對 GPU 的阻塞情況。常見的 CPU 計算操作如下:
數(shù)據(jù)加載
數(shù)據(jù)預處理
模型保存
loss 計算
評估指標計算
日志打印
指標上報
進度上報
三、常見 GPU 利用率低原因分析
1、數(shù)據(jù)加載相關
1)存儲和計算跨城了,跨城加載數(shù)據(jù)太慢導致 GPU 利用率低
說明:例如數(shù)據(jù)存儲在“深圳 ceph”,但是 GPU 計算集群在“重慶”,那就涉及跨城使用了,影響很大。
優(yōu)化:要么遷移數(shù)據(jù),要么更換計算資源,確保存儲及計算是同城的。
2)存儲介質性能太差
說明:不同存儲介質讀寫性能比較:本機 SSD > ceph > cfs-1.5 > hdfs > mdfs
優(yōu)化:將數(shù)據(jù)先同步到本機 SSD,然后讀本機 SSD 進行訓練。本機 SSD 盤為“/dockerdata”,可先將其他介質下的數(shù)據(jù)同步到此盤下進行測試,排除存儲介質的影響。
3)小文件太多,導致文件 io 耗時太長
說明:多個小文件不是連續(xù)的存儲,讀取會浪費很多時間在尋道上
優(yōu)化:將數(shù)據(jù)打包成一個大的文件,比如將許多圖片文件轉成一個 hdf5/pth/lmdb/TFRecord 等大文件
其他格式轉換方式請自行谷歌
4)未啟用多進程并行讀取數(shù)據(jù)
說明:未設置 num_workers 等參數(shù)或者設置的不合理,導致 cpu 性能沒有跑起來,從而成為瓶頸,卡住 GPU
優(yōu)化:設置 torch.utils.data.DataLoader 方法的 num_workers 參數(shù)、tf.data.TFRecordDataset 方法的 num_parallel_reads 參數(shù)或者 tf.data.Dataset.map 的 num_parallel_calls 參數(shù)。
5)未啟用提前加載機制來實現(xiàn) CPU 和 GPU 的并行
說明:未設置 prefetch_factor 等參數(shù)或者設置的不合理,導致 CPU 與 GPU 在時間上串行,CPU 運行時 GPU 利用率直接掉 0
優(yōu)化:設置 torch.utils.data.DataLoader 方法的 prefetch_factor 參數(shù) 或者 tf.data.Dataset.prefetch()方法。prefetch_factor 表示每個 worker 提前加載的 sample 數(shù)量 (使用該參數(shù)需升級到 pytorch1.7 及以上),Dataset.prefetch()方法的參數(shù) buffer_size 一般設置為:tf.data.experimental.AUTOTUNE,從而由 TensorFlow 自動選擇合適的數(shù)值。
6)未設置共享內存 pin_memory
說明:未設置 torch.utils.data.DataLoader 方法的 pin_memory 或者設置成 False,則數(shù)據(jù)需從 CPU 傳入到緩存 RAM 里面,再給傳輸?shù)?GPU 上
優(yōu)化:如果內存比較富裕,可以設置 pin_memory=True,直接將數(shù)據(jù)映射到 GPU 的相關內存塊上,省掉一點數(shù)據(jù)傳輸時間
2、數(shù)據(jù)預處理相關
1)數(shù)據(jù)預處理邏輯太復雜
說明:數(shù)據(jù)預處理部分超過一個 for 循環(huán)的,都不應該和 GPU 訓練部分放到一起
優(yōu)化:
a、設置 tf.data.Dataset.map 的 num_parallel_calls 參數(shù),提高并行度,一般設置為 tf.data.experimental.AUTOTUNE,可讓 TensorFlow 自動選擇合適的數(shù)值。
b、將部分數(shù)據(jù)預處理步驟挪出訓練任務,例如對圖片的歸一化等操作,提前開啟一個 spark 分布式任務或者 cpu 任務處理好,再進行訓練。
c、提前將預處理部分需要用到的配置文件等信息加載到內存中,不要每次計算的時候再去讀取。
d、關于查詢操作,多使用 dict 加速查詢操作;減少 for、while 循環(huán),降低預處理復雜度。
2)利用 GPU 進行數(shù)據(jù)預處理 -- Nvidia DALI
說明:Nvidia DALI 是一個專門用于加速數(shù)據(jù)預處理過程的庫,既支持 GPU 又支持 CPU
優(yōu)化:采用 DALI,將基于 CPU 的數(shù)據(jù)預處理流程改造成用 GPU 來計算
DALI 文檔如下:https://zhuanlan.zhihu.com/p/105056158
3、模型保存相關
1)模型保存太頻繁
說明:模型保存為 CPU 操作,太頻繁容易導致 GPU 等待
優(yōu)化:減少保存模型(checkpoint)的頻率
4、指標相關
1)loss 計算太復雜
說明:含有 for 循環(huán)的復雜 loss 計算,導致 CPU 計算時間太長從而阻塞 GPU
優(yōu)化:該用低復雜度的 loss 或者使用多進程或多線程進行加速
2)指標上報太頻繁
說明:指標上報操作太頻繁,CPU 和 GPU 頻繁切換導致 GPU 利用率低
優(yōu)化:改成抽樣上報,例如每 100 個 step 上報一次
5、日志相關
1)日志打印太頻繁
說明:日志打印操作太頻繁,CPU 和 GPU 頻繁切換導致 GPU 利用率低
優(yōu)化:改成抽樣打印,例如每 100 個 step 打印一次
四、常見數(shù)據(jù)加載方法說明
1、pytorch 的 torch.utils.data.DataLoader
DataLoader(dataset,batch_size=1,shuffle=False,sampler=None, batch_sampler=None,num_workers=0,collate_fn=None, pin_memory=False,drop_last=False,timeout=0, worker_init_fn=None,*,prefetch_factor=2, persistent_workers=False)
從參數(shù)定義中,我們可以看到 DataLoader 主要支持以下幾個功能:
支持加載 map-style 和 iterable-style 的 dataset,主要涉及到的參數(shù)是 dataset
自定義數(shù)據(jù)加載順序,主要涉及到的參數(shù)有 shuffle, sampler, batch_sampler, collate_fn
自動把數(shù)據(jù)整理成 batch 序列,主要涉及到的參數(shù)有 batch_size, batch_sampler, collate_fn, drop_last
單進程和多進程的數(shù)據(jù)加載,主要涉及到的參數(shù)有 num_workers, worker_init_fn
自動進行鎖頁內存讀取 (memory pinning),主要涉及到的參數(shù) pin_memory
支持數(shù)據(jù)預加載,主要涉及的參數(shù) prefetch_factor
參考文檔:https://pytorch.org/docs/stable/data.html
2、tensorflow 的 tf.data.Dataset
ds_train = tf.data.Dataset.from_tensor_slices((x,y)) .shuffle(5000) .batch(batchs) .map(preprocess,num_parallel_calls=tf.data.experimental.AUTOTUNE) .prefetch(tf.data.experimental.AUTOTUNE)
Dataset.prefetch(): 可以讓數(shù)據(jù)集對象 Dataset 在 ? 訓練時預取出若干個元素,使得在 GPU 訓練的同時 CPU 可以準備數(shù)據(jù),提升訓練流程的效率
Dataset.map(f): 轉換函數(shù) f 映射到數(shù)據(jù)集每一個元素; 可以利用多 CPU 資源,充分利用多核心的優(yōu)勢對數(shù)據(jù)進行并行化變換, num_parallel_calls 設置為 tf.data.experimental.AUTOTUNE 以讓 TensorFlow 自動選擇合適的數(shù)值,數(shù)據(jù)轉換過程多進程執(zhí)行,設置 num_parallel_calls 參數(shù)能發(fā)揮 cpu 多核心的優(yōu)勢
Dataset.shuffle(buffer_size): 將數(shù)據(jù)集打亂,取出前 buffer_size 個元素放入,并從緩沖區(qū)中隨機采樣,采樣后的數(shù)據(jù)用后續(xù)數(shù)據(jù)替換
Dataset.batch(batch_size):將數(shù)據(jù)集分成批次,即對每 batch_size 個元素,使用 tf.stack() 在第 0 維合并,成為一個元素
五、分布式任務常見的 GPU 利用率低問題
分布式任務相比單機任務多了一個機器間通信環(huán)節(jié)。如果在單機上面運行的好好的,擴展到多機后出現(xiàn) GPU 利用率低,運行速度慢等問題,大概率是機器間通信時間太長導致的。請排查以下幾點:
1、機器節(jié)點是否處在同一 modules?
答:機器節(jié)點處于不同 modules 時,多機間通信時間會長很多,deepspeed 組件已從平臺層面增加調度到同一 modules 的策略,用戶不需要操作;其他組件需聯(lián)系我們開啟。
2、多機時是否啟用 GDRDMA?
答:能否啟用 GDRDMA 和 NCCL 版本有關,經測試,使用 PyTorch1.7(自帶 NCCL2.7.8)時,啟動 GDRDMA 失敗,和 Nvidia 的人溝通后確定是 NCCL 高版本的 bug,暫時使用的運行注入的方式來修復;使用 PyTorch1.6(自帶 NCCL2.4.8)時,能夠啟用 GDRDMA。經測試,“NCCL2.4.8 + 啟用 GDRDMA ” 比 “NCCL2.7.8 + 未啟用 GDRDMA”提升 4%。通過設置 export NCCL_DEBUG=INFO,查看日志中是否出現(xiàn)[receive] via NET/IB/0/GDRDMA 和 [send] via NET/IB/0/GDRDMA,出現(xiàn)則說明啟用 GDRDMA 成功,否則失敗。
3、pytorch 數(shù)據(jù)并行是否采用 DistributedDataParallel ?
答:PyTorch 里的數(shù)據(jù)并行訓練,涉及 nn.DataParallel (DP) 和nn.parallel.DistributedDataParallel (DDP) ,我們推薦使用 nn.parallel.DistributedDataParallel (DDP)。
審核編輯:湯梓紅
-
cpu
+關注
關注
68文章
10804瀏覽量
210828 -
存儲
+關注
關注
13文章
4226瀏覽量
85575 -
gpu
+關注
關注
28文章
4673瀏覽量
128592 -
計算
+關注
關注
2文章
442瀏覽量
38704 -
利用率
+關注
關注
0文章
10瀏覽量
7029
原文標題:實踐教程|GPU 利用率低常見原因分析及優(yōu)化
文章出處:【微信號:3D視覺工坊,微信公眾號:3D視覺工坊】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論