xmake是一個基於Lua的輕量級現代化c/c++的項目構建工具,主要特色是:語法簡單易上手,提供更加可讀的項目維護,實現跨平臺行爲一致的構建體驗。html
本文咱們會詳細介紹下如何經過xmake來構建cuda程序以及與c/c++程序混合編譯。linux
首先,咱們須要安裝NVIDIA提供的Cuda Toolkit SDK工具,其相關說明以及安裝文檔,可參考官方文檔:CUDA Toolkit Documentation。c++
下載安裝好Cuda SDK後,在macosx上會默認安裝到/Developer/NVIDIA/CUDA-x.x
目錄下,Windows上能夠經過CUDA_PATH
的環境變量找到對應的SDK目錄,而 Linux下默認會安裝到/usr/local/cuda
目錄下。git
一般,xmake都能自動檢測到默認的cuda安裝環境,並不須要作任何操做,只須要執行xmake
命令就能夠自動完成編譯,固然若是找不到SDK,咱們也能夠手動指定Cuda SDK環境目錄:github
$ xmake f --cuda=/usr/local/cuda-9.1/
複製代碼
或者經過xmake g/global
命令切到全局設置,避免每次切換編譯模式都要從新配置一遍。macos
$ xmake g --cuda=/usr/local/cuda-9.1/
複製代碼
若是想要測試xmake對當前cuda環境的探測支持,能夠直接運行:windows
$ xmake l detect.sdks.find_cuda
{
linkdirs = {
"/Developer/NVIDIA/CUDA-10.2/lib/stubs",
"/Developer/NVIDIA/CUDA-10.2/lib"
},
bindir = "/Developer/NVIDIA/CUDA-10.2/bin",
sdkdir = "/Developer/NVIDIA/CUDA-10.2",
includedirs = {
"/Developer/NVIDIA/CUDA-10.2/include"
}
}
複製代碼
你們也能夠幫忙貢獻相關檢測代碼find_cuda.lua來改進xmake的檢測機制。bash
接下來,咱們就能夠建立一個空工程來快速體驗下了,xmake自帶了cuda的工程模板,只須要指定對應的語言便可建立cuda項目:工具
$ xmake create -l cuda test
create test ...
[+]: xmake.lua
[+]: src/main.cu
[+]: .gitignore
create ok!
複製代碼
默認建立的cuda工程,就是一個最簡單的基於Cuda的hello world工程,其源碼結構以下:測試
├── src
│ └── main.cu
└── xmalua
複製代碼
而xmake.lua裏面的內容咱們也能夠簡單看下:
-- define target
target("test")
set_kind("binary")
add_files("src/*.cu")
-- generate SASS code for SM architecture of current host
add_cugencodes("native")
-- generate PTX code for the virtual architecture to guarantee compatibility
add_cugencodes("compute_30")
複製代碼
能夠看到,除了最基本的.cu源文件添加,跟其餘c/c++項目惟一的區別就是多了個add_cugencodes()
用來設置cuda須要的gencodes,關於這塊,下面會詳細講解。
工程建立好後,只須要簡單的執行xmake便可完成編譯。
$ xmake
[00%]: ccache compiling.release src/main.cu
[99%]: devlinking.release test_gpucode.cu.o
[100%]: linking.release test
複製代碼
須要注意的是:從v2.2.7版本開始,xmake默認構建會啓用device-link的構建行爲,也就是說,如今編譯過程當中,會額外增長一步device-link過程:
[100%]: devlinking.release test_gpucode.cu.o
複製代碼
按照官方的說法,啓用device-link設備代碼連接的主要優勢是能夠爲您的應用程序提供更傳統的代碼結構,尤爲是在C++中,在現有項目結構不變的前提下,控制每一個構建和連接步驟,方便快速的啓用GPU代碼,實現混合編譯。
關於這塊可參看NVIDIA的官方描述:Separate Compilation and Linking of CUDA C++ Device Code) 若是要禁用device-link的構建邏輯,能夠經過add_values("cuda.devlink", false)
來設置禁用它。
固然,咱們也能夠嘗試直接運行這個cuda程序:
$ xmake run
複製代碼
而且若是設置了裏面值爲native,那麼xmake會自動探測當前主機的cuda設備對應的gencode。
這個接口主要用於添加cu代碼相關的編譯選項,咱們若是還須要一些更加定製化的設置flags,那麼就能夠調用add_cuflags
來直接設置更加原始的編譯選項,就比如c/c++中的add_cxflags
。
例如:
add_cuflags("-gencode arch=compute_30,code=sm_30")
複製代碼
這個接口主要用於添加cuda設備連接選項,因爲上文所說,2.2.7以後,xmake對於cuda程序的默認構建行爲會使用device-link,這個階段若是要設置一些連接flags,則能夠經過這個接口來設置。 由於最終的程序連接,會使用ldflags,不會調用nvcc,直接經過gcc/clang等c/c++連接器來連接,因此device-link這個獨立的連接階段的flags設置,經過這個接口來完成。
add_culdflags("-gencode arch=compute_30,code=sm_30")
複製代碼
add_cugencodes()
接口其實就是對add_cuflags("-gencode arch=compute_xx,code=compute_xx")
編譯flags設置的簡化封裝,其內部參數值對應的實際flags映射關係以下:
- compute_xx --> `-gencode arch=compute_xx,code=compute_xx`
- sm_xx --> `-gencode arch=compute_xx,code=sm_xx`
- sm_xx,sm_yy --> `-gencode arch=compute_xx,code=[sm_xx,sm_yy]`
- compute_xx,sm_yy --> `-gencode arch=compute_xx,code=sm_yy`
- compute_xx,sm_yy,sm_zz --> `-gencode arch=compute_xx,code=[sm_yy,sm_zz]`
- native --> match the fastest cuda device on current host,
eg. for a Tesla P100, `-gencode arch=compute_60,code=sm_60` will be added,
if no available device is found, no `-gencode` flags will be added
複製代碼
例如:
add_cugencodes("sm_30")
複製代碼
就等價爲
add_cuflags("-gencode arch=compute_30,code=sm_30")
add_culdflags("-gencode arch=compute_30,code=sm_30")
複製代碼
是否是上面的更加精簡些,這其實就是個用於簡化設置的輔助接口。
而若是咱們設置了native值,那麼xmake會自動探測當前主機的cuda設備,而後快速匹配到它對應的gencode設置,自動追加到整個構建過程當中。
例如,若是咱們主機目前的GPU是Tesla P100,而且可以被xmake自動檢測到,那麼下面的設置:
add_cugencodes("native")
複製代碼
等價於:
add_cugencodes("sm_60")
複製代碼
對於混合編譯,咱們只須要經過add_files
接口繼續加上對應的c/c++代碼文件就好了,是否是很簡單?
target("test")
set_kind("binary")
add_files("src/*.cu")
add_files("src/*.c", "src/*.cpp")
add_cugencodes("native")
複製代碼
nvcc在編譯內部的c/c++代碼時候,其實會調用主機環境的c/c++編譯器來編譯,好比linux下會默認使用gcc/g++,macos下默認使用clang/clang++,windows上默認使用cl.exe。 若是想要讓nvcc採用其餘的編譯器,好比在linux下改用clang做爲默認的c/c++編譯器,則須要指定--ccbin=
參數設置,這塊能夠看下:compiler-ccbin
而在xmake中,也對其進行了支持,只須要設置xmake f --cu-ccbin=clang
就能夠切換到其餘編譯器。
還有兩個跟cuda相關的編譯參數,我就簡單介紹下:
xmake f --cu=nvcc --cu-ld=nvcc
複製代碼
其中--cu
用來設置.cu代碼的編譯器,默認就是nvcc,不過clang如今也支持對.cu代碼的編譯,能夠切換設置來嘗試,--cu-ld
是設置device-link的連接器,而最終的總體程序link過程,仍是用的--ld
來連接的。