1. 前言
深度學習、圖像渲染、科學計算、挖礦這些復雜計算的場景都需要使用GPU進行大量計算,但是當你拿到一台GPU服務器以后,你應該如何入手學習呢,如何進行調試呢。本文主要講解一些GPU相關的知識,從GPU簡單介紹開始,進而到linux下如何查看GPU相關指標,最后講解如何調試調用GPU,並使用GPU運行簡單程序。
注:本文講解使用的GPU是NVIDIA GPU。
2. GPU知識介紹
2.1 GPU簡單介紹
GPU全稱graphics processing unit,中文譯名圖形處理器,又稱顯示核心、視覺處理器、顯示芯片。通常一般的程序任務直接是由CPU完成,但是對於密集型計算任務,就需要借助GPU來完成了。我們對CPU結構非常熟悉,一顆處理器其實由幾個或幾十個運算核心組合而成的,而GPU卻擁有上百顆甚至上千個運算核心,所以GPU具有強大的計算能力。
2.2 CUDA簡單介紹
要使用GPU進行計算,就需要有接口來調用GPU,CUDA就實現了完整的GPU調度方案。CUDA是NVIDIA公司推出的一種基於GPU的通用並行計算平台,提供了硬件的直接訪問接口。CUDA采用C語言作為編程語言提供大量的高性能計算指令開發能力,使開發者能夠在GPU的強大計算能力的基礎上建立起一種效率更高的密集數據計算解決方案。
上面簡要對GPU做了個介紹,下面直接進入實操部分吧。
3. linux的GPU查看
3.1 查看GPU硬件
查看GPU簡略信息
[root@gpu-node ~]# lspci | grep -i vga | grep -i nvidia
可以看到服務器有8塊NVIDIA的顯卡
查看某一塊顯卡的具體詳細信息
[root@gpu-node ~]# lspci -v -s 04:00.0
直接查看所有顯卡詳細信息-1
[root@gpu-node ~]# lspci -vnn | grep -i vga -A 12
直接查看所有顯卡詳細信息-2
[root@gpu-node ~]# lshw -C display
查看系統所使用的顯卡驅動名稱
[root@gpu-node ~]# lshw -c video | grep configuration
3.2 安裝顯卡驅動
禁用系統默認顯卡驅動nouveau
nouveau是一個第三方開源的NVIDIA驅動,一般Linux系統安裝的時候都會默認安裝這個驅動,這個驅動會與NVIDIA官方的驅動沖突。
[root@gpu-node ~]# lsmod | grep nouveau
[root@gpu-node ~]# vim /usr/lib/modprobe.d/nvidia.conf
blacklist nouveau
options nouveau modeset=0
# Uncomment the following line to enable kernel modesetting support.
# There is NO graphical framebuffer (like OSS drivers) at the moment; this is
# only for Wayland. For Gnome, you also require an EGLStream build of Mutter.
# options nvidia-drm modeset=1
建立新的鏡像
[root@gpu-node ~]# dracut /boot/initramfs-$(uname -r).img $(uname -r) --force
[root@gpu-node ~]# reboot
[root@gpu-node ~]# lsmod | grep nouveau
從NVIDIA官網:https://www.nvidia.com/Download/Find.aspx 下載對應顯卡版本的驅動安裝版本包。
[root@gpu-node ~]# wget https://us.download.nvidia.com/XFree86/Linux-x86_64/460.56/NVIDIA-Linux-x86_64-460.56.run
[root@gpu-node ~]# chmod +x NVIDIA-Linux-x86_64-460.56.run
安裝系統對應的gcc和kernel-devel,驅動在安裝過程種需要編譯kernel module
[root@gpu-node ~]# yum install -y gcc kernel-devel-$(uname -r)
安裝dkms,注冊nvidia驅動到dkms中,通過dkms管理,當內核更新的時候,會自動build新的nvidia內核模塊
[root@gpu-node ~]# yum install -y dkms
[root@gpu-node ~]# ./NVIDIA-Linux-x86_64-460.56.run --dkms --silent
--silent 靜默安裝,不彈出圖形化UI界面
安裝成功后可以執行 nvidia-smi
如果要卸載可以執行
[root@gpu-node ~]# ./NVIDIA-Linux-x86_64-460.56.run --uninstall --silent
查看是否有nvidia軟件包,可以yum remove卸載干凈
[root@gpu-node ~]# rpm -qa | grep -i nvidia
安裝后驗證
查看系統已安裝的NVIDIA module
[root@gpu-node ~]# lsmod|grep nvidia
查看nvidia的路徑與版本等信息
[root@gpu-node ~]# modinfo nvidia
3.3 nvidia-smi的使用
nvidia-smi可以直接查看GPU當前使用情況
[root@gpu-node ~]# nvidia-smi
輸出解釋:
GPU:表示顯卡編號,從0開始;
Fan:表示風扇轉速,數值在0到100%之間,是計算機期望的風扇轉速,如果計算機不是通過風扇冷卻或者風扇壞了,顯示出來就是N/A;
Name:表示顯卡名稱;
Temp:表示顯卡內部的溫度,單位是攝氏度,GPU溫度過高會導致GPU頻率下降;
Perf:表示性能狀態,從P0到P12,P0表示最大性能,P12表示狀態最小性能;
Pwr:表示能耗,上方的Persistence-M表示是持續模式的狀態,持續模式雖然耗能大,但是在新的GPU應用啟動時,花費的時間更少,這里顯示的是off的狀態;
Bus-Id:表示GPU總線的相關信息,domain:bus:device.function;
Disp.A:即Display Active,表示GPU的顯示是否初始化;
Memory Usage:表示顯存使用率;
Volatile GPU-Util:浮動的GPU利用率;
ECC:是否開啟錯誤檢查和糾正技術,0/DISABLED, 1/ENABLED;
Compute M:計算模式0/DEFAULT,1/EXCLUSIVE_PROCESS,2/PROHIBITED;
最下邊一欄的Processes表示每塊GPU上每個進程所使用的顯存情況,其中Type 有C和G,C表示計算的進程,G表示圖像處理的進程。顯卡占用和GPU占用是兩個不一樣的東西,顯卡是由GPU和顯卡等組成的,顯卡和GPU的關系有點類似於內存和CPU的關系,兩個指標的占用率不一定是互相對應的。
結合watch命令動態監控GPU使用情況
[root@gpu-node ~]# watch -n 1 nvidia-smi
3.4 安裝cuda
cuda下載地址:https://developer.nvidia.com/cuda-toolkit-archive
[root@gpu-node ~]# wget https://developer.download.nvidia.com/compute/cuda/11.2.0/local_installers/cuda-repo-rhel7-11-2-local-11.2.0_460.27.04-1.x86_64.rpm
[root@gpu-node ~]# rpm -ivh cuda-repo-rhel7-11-2-local-11.2.0_460.27.04-1.x86_64.rpm
[root@gpu-node ~]# yum install -y cuda
查看安裝信息
[root@gpu-node ~]# /usr/local/cuda/bin/nvcc -V
4. 程序調用GPU
下面講解如何使用python程序調用GPU
4.1 pycuda安裝
pycuda是一個可以訪問NVIDIA的CUDA的python庫
[root@gpu-node ~]# pip3 install pycuda==2018.1.1 -i https://pypi.mirrors.ustc.edu.cn/simple/
可能會出現如下錯誤信息:
gcc -pthread -Wno-unused-result -Wsign-compare -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -O3 -DNDEBUG -fPIC -DBOOST_ALL_NO_LIB=1 -DBOOST_THREAD_BUILD_DLL=1 -DBOOST_MULTI_INDEX_DISABLE_SERIALIZATION=1 -DBOOST_PYTHON_SOURCE=1 -Dboost=pycudaboost -DBOOST_THREAD_DONT_USE_CHRONO=1 -DPYGPU_PACKAGE=pycuda -DPYGPU_PYCUDA=1 -DHAVE_CURAND=1 -Isrc/cpp -Ibpl-subset/bpl_subset -I/usr/local/lib64/python3.6/site-packages/numpy/core/include -I/usr/include/python3.6m -c src/cpp/cuda.cpp -o build/temp.linux-x86_64-3.6/src/cpp/cuda.o
In file included from src/cpp/cuda.cpp:4:0:
src/cpp/cuda.hpp:14:18: fatal error: cuda.h: No such file or directory
#include <cuda.h>
^
編譯中斷。
error: command 'gcc' failed with exit status 1
----------------------------------------
Command "/usr/bin/python3 -u -c "import setuptools, tokenize;__file__='/tmp/pip-build-slw8h082/pycuda/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-ig9vl5ig-record/install-record.txt --single-version-externally-managed --compile" failed with error code 1 in /tmp/pip-build-slw8h082/pycuda/
出現上面報錯是因為cuda的相關的命令沒有加入到環境變量
添加環境變量即可
[root@gpu-node ~]# export PATH=/usr/local/cuda-11.2/bin:/usr/local/cuda/bin:$PATH
4.2 程序調用
我的第一個GPU調用程序小例子
vim gpu.py
import pycuda.autoinit
from pycuda.compiler import SourceModule
import time
mod = SourceModule("""
#include <stdio.h>
__global__ void work()
{
printf("Manage GPU success!\\n");
}
""")
func = mod.get_function("work")
func(block=(1,1,1))
time.sleep(30)
執行python gpu.py
使用 nvidia-smi 查看可以發現Process里面有進程使用GPU顯卡,說明我們的程序調用GPU成功
默認是使用第0個GPU,如果要使用其他GPU:
1.可以使用CUDA_VISIBLE_DEVICES=1 python3 gpu.py表示使用第一個1塊顯卡;
2.可以使用CUDA_DEVICE=0,1,2 python3 gpu.py表示使用第0塊、1塊、2塊顯卡,當第0塊顯卡顯存使用完畢后,會自動使用第1塊,以此類推;
3.或者直接將調用的設置寫在程序里
import time,os
os.environ["CUDA_VISIBLE_DEVICES"] = "2"
注意必須寫在 import pycuda.autoinit 前面
進化版:同時使用多個GPU
work.py
import time,os
os.environ["CUDA_VISIBLE_DEVICES"] = "0,1,2,3,4,5,6,7"
import pycuda.autoinit
from pycuda.compiler import SourceModule
mod = SourceModule("""
#include <stdio.h>
__global__ void work()
{
printf("Manage GPU success!\\n");
}
""")
func = mod.get_function("work")
func(block=(1,1,1))
time.sleep(60)
mul_gpu.py
import os,threading
cmd = "export PATH=/usr/local/cuda-11.2/bin:/usr/local/cuda/bin:$PATH;/usr/bin/python3 /root/mul_gpu.py"
threads = []
def test():
os.system(cmd)
for i in range(80):
t = threading.Thread(target=test,args=())
t.start()
threads.append(t)
for i in threads:
i.join()
運行 python3 mul_gpu.py
可以看到跑多個任務時,第0塊GPU跑滿后,會接着調度第1塊GPU。