在单独的编译模式下连接cuda代码

时间:2014-01-19 04:18:54

标签: matlab cuda mex

我正在尝试在linux下使用MATLAB mex中的CUDA代码。使用“整个程序编译”模式,它对我有用。我在Nsight内部采取以下两个步骤:

(1)将“-fPIC”作为编译器选项添加到每个.cpp或.cu文件中,然后单独编译它们,每个都生成一个.o文件。

(2)将链接器命令设置为“mex”并添加“-cxx”以指示所有.o输入文件的类型是cpp文件,并添加cuda的库路径。还要添加一个包含mexFunction条目的cpp文件作为附加输入。

这很好用,结果mex文件在MATLAB下运行良好。之后,当我需要使用动态并行时,我必须切换到Nsight中的“单独编译模式”。我尝试了同样的事情,但链接器产生了很多错过引用的错误,我无法解决。

然后我检查了“单独编译”模式的编译和链接步骤。我对它的作用感到困惑。似乎Nsight为每个.cpp或.cu文件执行两个编译步骤,并生成.o文件和.d文件。像这样:

/usr/local/cuda-5.5/bin/nvcc -O3 -gencode arch=compute_35,code=sm_35 -odir "src" -M -o "src/tn_matrix.d" "../src/tn_matrix.cu"
/usr/local/cuda-5.5/bin/nvcc --device-c -O3 -gencode arch=compute_35,code=compute_35 -gencode arch=compute_35,code=sm_35  -x cu -o  "src/tn_matrix.o" "../src/tn_matrix.cu"

链接命令是这样的:

/usr/local/cuda-5.5/bin/nvcc --cudart static --relocatable-device-code=true -gencode arch=compute_35,code=compute_35 -gencode arch=compute_35,code=sm_35 -link -o  "test7"  ./src/cu_base.o ./src/exp_bp_wsj_dev_mex.o ./src/tn_main.o ./src/tn_matlab_helper.o ./src/tn_matrix.o ./src/tn_matrix_lib_dev.o ./src/tn_matrix_lib_host.o ./src/tn_model_wsj_dev.o ./src/tn_model_wsj_host.o ./src/tn_utility.o   -lcudadevrt -lmx -lcusparse -lcurand -lcublas

有趣的是链接器不将.d文件作为输入。所以我不确定它是如何处理这些文件的,以及如何在链接时使用“mex”命令处理它们?

另一个问题是链接阶段有很多我不理解的选项(--cudart static --relocatable-device-code = true),我想这就是为什么我不能让它像“整个程序编译”模式。所以我尝试了以下内容:

(1)以与帖子开头相同的方式编译。

(2)保留Nsight提供的链接命令,但更改为使用“-shared”选项,以便链接器生成lib文件。

(3)通过输入lib文件和包含mexFunction条目的另一个cpp文件来调用mex。

这种方式mex编译工作,它产生一个mex可执行文件作为输出。但是,在MATLAB下运行生成的mex可执行文件会立即产生分段错误并导致MATLAB崩溃。

我不确定这种链接方式是否会导致任何问题。更奇怪的是,我发现mex链接步骤似乎完全没有检查可执行文件的完整性,因为即使我错过了mexFunction将使用的某些函数的.cpp文件,它仍然可以编译。

编辑:

我想出了如何手动链接到可以在MATLAB下正确运行的mex可执行文件,但我还没想出如何在Nsight下自动执行,我可以在“整个程序编译”模式下。这是我的方法:

(1)从构建中排除包含mexFunction条目的cpp文件。使用命令“mex -c”手动编译它。

(2)将“-fPIC”作为编译器选项添加到其余的.cpp或.cu文件中,然后单独编译,每个都生成.o文件。

(3)链接失败,因为找不到主函数。我们没有它,因为我们使用mexFunction并且它被排除在外。这没关系,我把它留在那里。

(4)按照以下帖子中的方法手动将.o文件链接到设备对象文件

cuda shared library linking: undefined reference to cudaRegisterLinkedBinary

例如,如果步骤(2)产生a.o和b.o,我们在这里做

nvcc -gencode arch=compute_35,code=sm_35 -Xcompiler '-fPIC' -dlink a.o b.o -o mex_dev.o -lcudadevrt

请注意,此处输出文件mex_dev.o不应存在,否则上述命令将失败。

(5)使用mex命令链接步骤(2)和步骤(4)中生成的所有.o文件,并提供所有必需的库。

这可以生成并运行可运行的mex可执行文件。我无法在Nsight中自动执行步骤(1)的原因是因为如果我将编译命令更改为“mex”,Nsight也将使用此命令生成依赖文件(问题文本中提到的.d文件)。我无法在Nsight中自动执行步骤(4)和步骤(5)的原因是因为它涉及两个命令,我不知道如何将它们放入。如果您知道如何执行这些操作,请告诉我。谢谢!

2 个答案:

答案 0 :(得分:3)

好的,我找到了解决方案。以下是在Nsight中使用“单独编译模式”编译mex程序的完整步骤:

  1. 创建一个cuda项目。
  2. 在项目级别,更改以下内容的构建选项:

    • 在项目级别的“NVCC编译器”的编译器选项中启用-fPIC
    • -dlink -Xcompiler '-fPIC'添加到链接器“NVCC Linker”的“专家设置”“命令行模式”
    • 将字母o添加到“构建工件” - > “工件扩展”,因为在最后一步中-dlink我们将输出设为.o文件。
    • mex -cxx -o path_to_mex_bin/mex_bin_filename ./*.o ./src/*.o -lcudadevrt添加到“Post Build Steps”,(添加其他必要的库)

    更新:在我的实际项目中,我将最后一步移动到MATLAB中的.m文件中,否则如果我在mex程序运行时执行此操作,则可能导致MATLAB崩溃。

  3. 对于需要使用mex编译的文件,请为每个文件更改这些构建选项:

    • 在工具链编辑器中将编译器更改为GCC C++ Compiler
    • 返回编译器设置GCC C++ Compiler并将命令更改为mex
    • 将命令行模式更改为${COMMAND} -c -outdir "src" ${INPUTS}
  4. 其他几点说明:

    (1)必须在mex编译器中隐藏Cuda特定的细节(例如内核函数和对内核函数的调用)。所以它们应该放在.cu文件而不是头文件中。这是一个将涉及cuda细节的模板放入.cu文件的技巧。

    在头文件(例如f.h)中,只放置函数的声明,如下所示:

    template<typename ValueType>
    void func(ValueType x);
    

    添加一个名为f.inc的新文件,其中包含定义

    template<>
    void func(ValueType x) {
      // possible kernel launches which should be hidden from mex
    }
    

    在源代码文件(例如f.cu)中,您将此

    #define ValueType float
    #include "f.inc"
    #undef ValueType
    
    #define ValueType double
    #include "f.inc"
    #undef ValueType
    
    // Add other types you want.
    

    这个技巧可以很容易地推广到模板化的类来隐藏细节。

    (2)mex特定细节也应该从cuda源文件中隐藏,因为mex.h将改变某些系统函数的定义,例如printf。所以包含“mex.h”不应出现在可能包含在cuda源文件中的头文件中。

    (3)在包含条目mexFunction的mex源代码文件中,可以使用编译器宏MATLAB_MEX_FILE来选择性地编译代码段。这样,源代码文件可以编译成mex可执行文件或通常可执行文件,允许在没有matlab的情况下在Nsight下进行调试。以下是在Nsight下构建多个目标的技巧:Building multiple binaries within one Eclipse project

答案 1 :(得分:1)

首先,应该可以将Night设置为使用自定义Makefile ,而不是自动生成它。请参阅Setting Nsight to run with existing Makefile project

一旦我们有了自定义Makefile,就可以自动化(1),(4)和(5)。自定义Makefile的优点是您确切知道将要发生的编译命令。

一个简单的例子:

all: mx.mexa64

mx.mexa64: mx.o
    mex -o mx.mexa64 mx.o -L/usr/local/cuda/lib64 -lcudart -lcudadevrt

mx.o: mxfunc.o helper.o
    nvcc -arch=sm_35 -Xcompiler -fPIC -o mx.o -dlink helper.o mxfunc.o -lcudadevrt

mxfunc.o: mxfunc.c
    mex -c -o mxfunc.o mxfunc.c

helper.o: helper.c
    nvcc -arch=sm_35 -Xcompiler -fPIC -c -o helper.o helper.c

clean:
    rm -fv mx.mexa64 *.o

...其中mxfunc.c包含mxFunctionhelper.c不包含。{/ p>

编辑:您可以在自动编译系统中实现相同的效果。右键单击每个源文件并选择属性,您将获得一个窗口,您可以在其中为该单个文件添加一些编译选项。对于链接选项,请打开项目的属性。做一些实验并注意控制台中显示的实际编译命令。根据我的经验,自定义选项有时会以一种奇怪的方式与自动系统交互。如果这个方法对你来说太麻烦了,我建议你制作一个自定义的Makefile;这样,至少我们不会受到意想不到的副作用。