NVIDIA CUDA C++ 編譯器 nvcc 基於每個內核,既可以用來產生特定於體系結構的 cubin 文件,又能產生前向兼容的 PTX 版本。
每個 cubin 文件針對特定的計算能力版本,並且僅與相同主要版本號的 GPU 架構向前兼容。
例如,針對計算能力 3.0 的 cubin 文件支持所有計算能力 3.x 設備,但不支持計算能力 5.x 或 6.x 設備。
基於這個原因,為了確保與應用程序發布后引入的 GPU 架構的向前兼容性,建議所有應用程序都包含其內核的 PTX 版本。
注意:CUDA 運行時應用程序同時包含針對給定體系結構的 cubin 和 PTX 代碼,默認情況下自動使用 cubin,嚴格保留 PTX 路徑以實現前向兼容性。
對於已經包含了其內核的 PTX 版本的應用程序應在基於 Volta 的 GPU 上原樣工作。而對於通過 cubin 文件僅支持特定 GPU 架構的應用程序,需要更新以提供與 Volta 兼容的 PTX 或 cubins 。
1. 不同NVIDIA顯卡對應的SM架構(CUDA arch and CUDA gencode)
1.1 NVIDIA的nvcc sm標志是干什么用的
使用NVCC編譯器編譯CUDA源文件時,架構標志位 -arch 指明了CUDA文件編譯產生的結果所依賴的NVIDIA GPU架構的名稱,而生成碼 -gencode 允許生成更多的PTX文件,並且對不同的架構可以重復許多次。
當編譯CUDA代碼時,只能根據一種架構進行編譯,用來匹配使用最多的GPU顯卡。
這使得運行時間最短,因為code generation總是發生在編譯期間,如果你只指明了-gencode而忽略了-arch,GPU code generation會由CUDA驅動在JIT編譯器產生。
若要加速CUDA編譯,就減少不相關-gencode標志的數量,然而有時我們卻希望更好的CUDA向后兼容性,只能添加更多的-gencode。
1.2 首先檢查你使用的GPU型號和CUDA版本
以下是支持的 sm 變量和相對應的典型顯卡型號
CUDA 7以上版本
- Fermi (CUDA 3.2 一直到 CUDA 8) (deprecated from CUDA 9):
SM20 or SM_20, compute_30 – 比較舊的顯卡 GeForce 400, 500, 600, GT-630
- Kepler (CUDA 5及以上):
SM30 or SM_30, compute_30 – Kepler architecture (generic – Tesla K40/K80, GeForce 700, GT-730)
Adds support for unified memory programming
SM35 or SM_35, compute_35 – More specific Tesla K40
Adds support for dynamic parallelism. Shows no real benefit over SM30 in my experience.
SM37 or SM_37, compute_37 – More specific Tesla K80
Adds a few more registers. Shows no real benefit over SM30 in my experience
- Maxwell (CUDA 6及以上版本):
SM50 or SM_50, compute_50 – Tesla/Quadro M series
SM52 or SM_52, compute_52 – Quadro M6000 ,
GeForce 900,
GTX-970, GTX-980, GTX Titan X
SM53 or SM_53, compute_53 – Tegra (Jetson) TX1 / Tegra X1
- Pascal (CUDA 8及以上版本)
SM60 or SM_60, compute_60 – Quadro GP100,
Tesla P100,
DGX-1 (Generic Pascal)
SM61 or SM_61, compute_61 – GTX 1080, GTX 1070, GTX 1060, GTX 1050, GTX 1030,
Titan Xp,
Tesla P40, Tesla P4,
Discrete GPU on the NVIDIA Drive PX2
SM62 or SM_62, compute_62 – Integrated GPU on the NVIDIA Drive PX2, Tegra (Jetson) TX2
- Volta (CUDA 9及以上版本)
SM70 or SM_70, compute_70 – DGX-1 with Volta,
Tesla V100,
GTX 1180 (GV104),
Titan V, Quadro GV100
SM72 or SM_72, compute_72 – Jetson AGX Xavier
- Turing (CUDA 10及以上版本)
SM75 or SM_75, compute_75 – GTX Turing – GTX 1660 Ti,
RTX 2060, RTX 2070, RTX 2080,
Titan RTX,
Quadro RTX 4000, Quadro RTX 5000, Quadro RTX 6000, Quadro RTX 8000
1.3 根據 NVIDIA 的官方說明
nvcc的 -gencode= 命令行選項的 arch= 指定前端編譯目標,並且必須始終為PTX版本。
code= 指定后端編譯目標,可以是cubin或PTX或兩者均可。
只有由 code= 指定的后端目標版本將保留在結果二進制文件中,至少包含一個PTX以提供Volta兼容。
1.4 參數示例
取得最大兼容性的 CUDA 7 標志示例
-arch=sm_30 -gencode=arch=compute_20,code=sm_20 -gencode=arch=compute_30,code=sm_30 -gencode=arch=compute_50,code=sm_50 -gencode=arch=compute_52,code=sm_52 -gencode=arch=compute_52,code=compute_52
CUDA 8
-arch=sm_30 -gencode=arch=compute_20,code=sm_20 -gencode=arch=compute_30,code=sm_30 -gencode=arch=compute_50,code=sm_50 \ -gencode=arch=compute_52,code=sm_52 -gencode=arch=compute_60,code=sm_60 \ -gencode=arch=compute_61,code=sm_61 -gencode=arch=compute_61,code=compute_61
CUDA 9 Volta 型號顯卡
-arch=sm_50 -gencode=arch=compute_50,code=sm_50 -gencode=arch=compute_52,code=sm_52 -gencode=arch=compute_60,code=sm_60 \ -gencode=arch=compute_61,code=sm_61 -gencode=arch=compute_70,code=sm_70 \ -gencode=arch=compute_70,code=compute_70
CUDA 10 Turing 型號顯卡
-arch=sm_50 -gencode=arch=compute_50,code=sm_50 -gencode=arch=compute_52,code=sm_52 -gencode=arch=compute_60,code=sm_60 \ -gencode=arch=compute_61,code=sm_61 -gencode=arch=compute_70,code=sm_70 \ -gencode=arch=compute_75,code=sm_75 -gencode=arch=compute_75,code=compute_75
2. 基於NVIDIA Volta架構為GPU構建 CUDA 應用程序
第一步:檢查 Volta 兼容的設備代碼編譯到了應用程序之中
2.1.1 使用 CUDA Toolkit 8.0 及之前版本的應用程序
使用CUDA Toolkit版本2.1至8.0構建的CUDA應用程序兼容Volta,只要構建時包含了其內核的PTX版本。可以通過如下步驟檢測:
- 下載安裝最新版驅動 http://www.nvidia.com/drivers
- 設置環境變量 CUDA_FORCE_PTX_JIT=1
- 登錄應用程序
第一次登錄CUDA應用程序時,CUDA驅動將會為每個CUDA內核進行JIT編譯PTX,在本地cubin代碼中使用。
如果按上述說明設置了環境變量登錄之后正常工作,說明已經成功驗證了Vlota兼容性。(注意:確保在驗證之后將CUDA_FORCE_PTX_JIT復位!)
2.1.2 使用CUDA Toolkit 9.0的應用程序
使用CUDA Toolkit 9.0構建的CUDA應用程序兼容Volta,只要構建時包含了Volta-native cubin格式的內核或PTX格式的內核或兩者都有。
第二步:構建 Volta 支持的應用程序
當一個 CUDA 應用程序登錄內核時,CUDA Runtime 會決定系統中每個 GPU 的計算能力,並利用這一信息自動尋找該內核最匹配的 cubin 或 PTX 版本。
如果 cubin 文件支持當前可用的目標 GPU 的體系架構,就是用該 cubin 文件;否則 CUDA Runtime 將加載 PTX ,並在登錄之前 JIT 編譯此 PTX 以得到本地 cubin 格式的 GPU cubin 文件。
如果兩者都不滿足,內核登錄失敗。
構建本地 cubin 格式或至少支持 Volta 的 PTX 的應用程序的方法取決於使用的 CUDA Toolkit 版本。
提供本地 cubin 文件的主要優勢如下:
節省了終端用於 JIT 編譯僅支持 PTX 的內核的時間。所有的內核編譯成應用程序之后在加載時必須要有本地二進制文件,或者將即刻從 PTX 進行編譯構建,包括來自所有庫文件的內核,這些庫文件鏈接到應用程序,
即使該應用程序永遠都不會登錄這些內核。特別是,當使用比較大的庫時,JIT 編譯過程將消耗相當的時間。
CUDA 驅動將緩存這些 PTX JIT 產生的 cubin 結果,這多數情況下對一個使用者只有一次時間消耗,但如果有可能我們還是希望避免。
PTX JIT 編譯內核通常並不能很好地利用較新的 GPU 的架構特征,也即是說本地編譯產生的代碼可能運行得更快或更准確。
2.2.1 使用 CUDA Toolkit 8.0 及之前版本的應用程序
CUDA Toolkit 8.0 或更早版本中包含的編譯器會生成 Maxwell 和 Pascal 等早期 NVIDIA 架構的本地 cubin 文件,但無法生成 Volta 架構的 cubin 文件。
為了在使用 8.0 或更早版本的 CUDA Toolkit 時支持 Volta 和將來的體系結構,編譯器必須為每個內核生成 PTX 版本。
下面是可以用來構建 mykernel.cu 的編譯器設置,mykernel.cu 可以在 Maxwell 或 Pascal 設備上本地運行,在 Volta 設備上通過 PTX JIT 運行。
注意
- compute_XX 指的是 PTX 版本
- sm_XX 指的是 cubin 版本
- nvcc 的 -gencode= 命令行選項的 arch= 指定前端編譯目標,並且必須始終為 PTX 版本。
- code= 指定后端編譯目標,可以是 cubin 或 PTX 或兩者均可。
- 只有由 code= 指定的后端目標版本將保留在結果二進制文件中,至少包含一個PTX以提供Volta兼容性。
Mac/Linux
/usr/local/cuda/bin/nvcc -gencode=arch=compute_50,code=sm_50 -gencode=arch=compute_52,code=sm_52 -gencode=arch=compute_60,code=sm_60 \ -gencode=arch=compute_61,code=sm_61 -gencode=arch=compute_61,code=compute_61 \ -O2 -o mykernel.o -c mykernel.cu
另外,你可能熟悉 nvcc 命令行選項 -arch=sm_XX,它的簡寫相當於上面使用的更明確的 -gencode= 命令行選項。
-arch=sm_XX 展開成如下形式
-gencode=arch=compute_XX,code=sm_XX
-gencode=arch=compute_XX,code=compute_XX
然而,雖然 -arch=sm_XX 命令行選項確在默認情況下導致包含PTX后端目標,它一次只能指定一個目標 cubin 體系結構,並且不能使用多個 -arch= 選項相同的 nvcc 命令行,這就是上面的示例顯式使用 -gencode= 的原因。
2.2.2 使用CUDA Toolkit 9.0的應用程序
使用CUDA Toolkit 9.0版本,nvcc可以生成Volta體系結構(計算能力7.0)的本地cubin文件。
使用CUDA Toolkit 9.0時,為了確保nvcc將為所有最新的GPU架構以及PTX版本生成cubin文件,以便於將來的GPU體系架構進行前向兼容,可以像下面的示例一樣在nvcc命令行指定適當的 -gencode= 參數。
Mac/Linux
/usr/local/cuda/bin/nvcc -gencode=arch=compute_50,code=sm_50 -gencode=arch=compute_52,code=sm_52 -gencode=arch=compute_60,code=sm_60 \ -gencode=arch=compute_61,code=sm_61 -gencode=arch=compute_70,code=sm_70 \ -gencode=arch=compute_70,code=compute_70 -O2 -o mykernel.o -c mykernel.cu
- compute_XX 指PTX版本
- sm_XX 指cubin版本
- nvcc的 -gencode= 命令行選項的 arch= 指定前端編譯目標,並且必須始終為 PTX 版本。
- code= 指定后端編譯目標,可以是 cubin 或 PTX 或兩者均可。
- 只有由 code= 指定的后端目標版本將保留在結果二進制文件中,至少包含一個 PTX 以提供未來體系架構的兼容性。
- 同時,注意 CUDA 9.0 移除了
- 對計算能力 2.x 設備的支持,任何 compute_2x 及 sm_2x 需要從編譯選項中移除。
2.2.3 獨立線程調度兼容性
Volta體系架構在線程束中引入了獨立線程調度。
如果開發人員對扭曲同步性做出了假設,那么與以前的體系架構相比,此功能可以更改參與執行的代碼的線程集合。
更多細節問題和正確操作請參考 CUDA C++ 編程指南 中的計算能力7.0部分。
為了幫助遷移,Volta開發人員可以通過下面編譯選項的組合選擇加入Pascal調度模型。
ncvv -arch=compute_60 -code=sm_70
參考資料
[1] Matching SM architectures (CUDA arch and CUDA gencode) for various NVIDIA cards