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
来连接的。