GPU并行运算,Matlab通过mex调用CUDA的方法



更新2018.06.14


最近有使用Matlab通过mex调用CUDA加速视频处理的需求,于是折腾了一下,网上的说法可谓千奇百怪众说纷纭,却没有能用的。经过六个多小时的反复搜索和尝试,本人终于成功编译运动了了matlab的mexCUDA例程:mexGPUExample.cu。

1.软件环境

这个过程涉及三个环境:Visual Studio、Cuda Toolkit和Matlab。其中Cuda依赖Visual Studio、Matlab依赖Cuda和Visual Studio。官方正式支持的版本关系如下表:

Matlab版本 CUDA版本 Visual Studio版本
2016a 8.0 2013
2017b 8.0 2013

本人用的Win7系统(64位),Matlab2017b、Cuda 8.0和VS2013。

2.步骤

Matlab通过mex使用CUDA可以分为三步:
1 首先准备好cuda kernel文件(Matlab 自带,我的在下面的位置):
“D:\Program Files\MATLAB\R2014b\toolbox\distcomp\gpu\extern\src\mex\mexGPUExample.cu”
2 编写一个testMexCuda.m文件,内容如下:

#设置环境变量 

setenv(‘CUDA_PATH’, ‘C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v7.5); 

setenv(‘CUDA_BIN_PATH’,‘C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v7.5\bin’); 

setenv(‘CUDA_LIB_PATH’, ‘C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v7.5\lib’);

编译(-v为显示详细信息,利于查错)

mex -v mexGPUExample.cu

运行测试程序

x = ones(4,4,‘gpuArray’);
y = mexGPUExample(x)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

注意如果你使用的CUDA版本不是8.0,请将8.0改为你的版本号(如7.5,6.5,6.0等)。

3 将mex_CUDA_win64.xml文件复制到testMexCuda.m文件所在的目录,并修改为内容如下
(”D:\Program Files\MATLAB\R2014b\toolbox\distcomp\gpu\extern\src\mex\win64\mex_CUDA_win64.xml”)

<?xml version="1.0" encoding="UTF-8" ?>
<!-- Copyright 2013-2014 The MathWorks, Inc. -->
<config
    Name="NVIDIA CUDA Compiler"
    ShortName="nvcc"
    Manufacturer="NVIDIA"
    Version="7.5"
    Language="CUDA"
    Priority="A"
    Location="$NVCC" >
    <Details
        CompilerExecutable="$COMPILER"
        CompilerDefines="$COMPDEFINES"
        CompilerFlags="$COMPFLAGS"
        OptimizationFlags="$OPTIMFLAGS"
        DebugFlags="$DEBUGFLAGS"
        IncludeFlags="$INCLUDE"
        LinkerExecutable="$LINKER"
        LinkerFlags="$LINKFLAGS"
        LinkerLibraries="$LINKLIBS"
        LinkerOptimizationFlags="$LINKOPTIMFLAGS"
        LinkerDebugFlags="$LINKDEBUGFLAGS"
        CommandLineShell="$VCVARSALLDIR\VCVARSALL.BAT "
        CommandLineShellArg=" amd64 "
        CompilerDefineFormatter="--compiler-options=/D%s"
        LinkerLibrarySwitchFormatter="lib%s.lib;%s.lib"
        LinkerPathFormatter="/LIBPATH:%s"
        LibrarySearchPath="$$LIB;$$LIBPATH;$$PATH;$$INCLUDE;$MATLABROOT\extern\lib\$ARCH\microsoft"
    />
    <!-- Switch guide: http://msdn.microsoft.com/en-us/library/fwkeyyhe(v=vs.71).aspx -->
    <vars
          CMDLINE100="$COMPILER -c $COMPFLAGS $OPTIM $COMPDEFINES $INCLUDE $SRC -o $OBJ"
          CMDLINE200="$LINKER $LINKFLAGS $LINKTYPE $LINKOPTIM $LINKEXPORT $LINKLIBS $OBJS /out:$EXE"
          CMDLINE250="mt -outputresource:$EXE;2 -manifest $MANIFEST"
          CMDLINE300="del $OBJ $EXP $LIB $MANIFEST $ILK"

          COMPILER="nvcc"
          COMPFLAGS="-gencode=arch=compute_20,code=sm_20 -gencode=arch=compute_30,code=sm_30 -gencode=arch=compute_50,code=&#92;&quot;sm_50,compute_50&#92;&quot; --compiler-options=/c,/GR,/W3,/EHs,/nologo,/MD"
          COMPDEFINES="--compiler-options=/D_CRT_SECURE_NO_DEPRECATE,/D_SCL_SECURE_NO_DEPRECATE,/D_SECURE_SCL=0,$MATLABMEX"
          MATLABMEX="/DMATLAB_MEX_FILE"
          OPTIMFLAGS="--compiler-options=/O2,/Oy-,/DNDEBUG"
          INCLUDE="-I&quot;$MATLABROOT\extern\include&amp;quot; -I&amp;quot;$MATLABROOT\simulink\include&quot;"
          DEBUGFLAGS="--compiler-options=/Z7"

          LINKER="link"
          LINKFLAGS="/nologo /manifest"
          LINKTYPE="/DLL "
          LINKEXPORT="/EXPORT:mexFunction"
          LINKLIBS="/LIBPATH:&quot;$MATLABROOT\extern\lib\$ARCH\microsoft&quot; libmx.lib libmex.lib libmat.lib gpu.lib cudart.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib  ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib"
          LINKDEBUGFLAGS="/debug /PDB:&quot;$TEMPNAME$LDEXT.pdb&quot;"
          LINKOPTIMFLAGS=""
          OBJEXT=".obj"
          LDEXT=".mexw64"
          SETENV="set COMPILER=$COMPILER 
                  set COMPFLAGS=$COMPFLAGS $COMPDEFINES 
                  set OPTIMFLAGS=$OPTIMFLAGS 
                  set DEBUGFLAGS=$DEBUGFLAGS 
                  set LINKER=$LINKER 
                  set LINKFLAGS=$LINKFLAGS /export:%ENTRYPOINT% $LINKTYPE $LINKLIBS $LINKEXPORT 
                  set LINKDEBUGFLAGS=/debug /PDB:&quot;%OUTDIR%%MEX_NAME%$LDEXT.pdb&quot; 
                  set NAME_OUTPUT=/out:&quot;%OUTDIR%%MEX_NAME%%MEX_EXT%&quot;"
    />
    <locationFinder>
        <VSROOT>
            <and>
                <envVarExists name="VS120COMNTOOLS" />
                <fileExists name="$$\..\..\VC\bin\amd64\cl.exe"</span> /&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-title">dirExists</span> <span class="hljs-attribute">name</span>=<span class="hljs-value">"$$\..\..\.." />
            </and>
        </VSROOT>
        <SDKROOT>
            <or>
                <hklmExists path="SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A" name="InstallationFolder" />
                <hkcuExists path="SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A" name="InstallationFolder" />
            </or>
        </SDKROOT>
        <PROF_ENV>
            <and>
                <envVarExists name="VS120COMNTOOLS" />
                <fileExists name="$$\..\IDE\devenv.exe"</span> /&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-title">not</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-title">fileExists</span> <span class="hljs-attribute">name</span>=<span class="hljs-value">"$$\..\IDE\VCExpress.exe" />
                </not>
            </and>
        </PROF_ENV>
        <VCVARSALLDIR>
            <and>
                <envVarExists name="VS120COMNTOOLS" />
                <fileExists name="$$\..\..\VC\vcvarsall.bat"</span> /&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-title">dirExists</span> <span class="hljs-attribute">name</span>=<span class="hljs-value">"$$"/>
            </and>
        </VCVARSALLDIR>
        <CUDA_LIB_PATH>
            <or>
              <and>
                <envVarExists name="CUDA_LIB_PATH"/>
                <fileExists name="$$\cudart.lib"</span> /&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-title">dirExists</span> <span class="hljs-attribute">name</span>=<span class="hljs-value">"$$" />
              </and>
              <and>
                <envVarExists name="CUDA_PATH"/>
                <fileExists name="$$\lib\x64\cudart.lib"</span> /&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-title">dirExists</span> <span class="hljs-attribute">name</span>=<span class="hljs-value">"$$" />
              </and>
            </or>
        </CUDA_LIB_PATH>
        <CUDA_BIN_PATH>
            <or>
                <and>
                    <envVarExists name="CUDA_BIN_PATH"/>
                    <fileExists name="$$\nvcc.exe"</span> /&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-title">dirExists</span> <span class="hljs-attribute">name</span>=<span class="hljs-value">"$$" />
                </and>
                <and>
                    <envVarExists name="CUDA_PATH"/>
                    <fileExists name="$$\bin\nvcc.exe"</span> /&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-title">dirExists</span> <span class="hljs-attribute">name</span>=<span class="hljs-value">"$$" />
                </and>
                <and>
                    <envVarExists name="MW_NVCC_PATH"/>
                    <fileExists name="$$\nvcc.exe"</span> /&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-title">dirExists</span> <span class="hljs-attribute">name</span>=<span class="hljs-value">"$$" />
                </and>
                <and>
                  <fileExists name="C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v7.5\bin\nvcc.exe" />
                  <dirExists name="$$" />
                </and>
            </or>
        </CUDA_BIN_PATH>
    </locationFinder>
    <env
        PATH="$CUDA_BIN_PATH;$VSROOT\VC\Bin\amd64;$VSROOT\VC\Bin\VCPackages;$VSROOT\Common7\IDE;$VSROOT\Common7\Tools;$VSROOT\Common7\Tools\bin;$SDKROOT\Bin\x64;$SDKROOT\Bin\win64\x64;$SDKROOT\Bin;$PATH"
        INCLUDE="$VSROOT\VC\ATLMFC\INCLUDE;$VSROOT\VC\INCLUDE;$SDKROOT\include;$MATLABROOT\extern\include;$MATLABROOT\toolbox\distcomp\gpu\extern\include"
        LIB="$VSROOT\VC\ATLMFC\LIB\amd64;$VSROOT\VC\Lib\amd64;$SDKROOT\Lib\X64;$MATLABROOT\lib\$ARCH;$CUDA_LIB_PATH;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib\x64"

LIBPATH="$VSROOT\VC\Lib\amd64;"
    />
</config>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
具体修改方式:
(1)VS环境变量: VS100COMNTOOLS=>VS120COMNTOOLS (141是VS2017,140是VS2015,120是VS2013,110是VS2012,100是VS2010)
(2)两个注册表值: “SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.0A” => “…\Windows\v7.1A”;如果用与本文不同的VS版本,请用注册表编辑器展开相应的注册表路径查看正确的版本号。
(3)两个变量值:最后的LIB和LIBPATH直接更改为上面文件中的值。
(4)CUDA版本号:将所有8.0修改为你的CUDA版本号,如7.5,6.5,6.0。
(5)CUDA计算能力兼容性:CUDA 8.0以上的nvcc编译器需要删掉-gencode=arch=compute_20,code=sm_20;另外,GTX 980Ti可以添加-gencode=arch=compute_52,code=sm_52,GTX 1080Ti可以添加-gencode=arch=compute_61,code=sm_61以获得更好的性能。具体需要自己查询你的显卡计算能力兼容性(CUDA Compute Compability)。

以上的第三点修改很重要,如果不修改,编译时会出现如下错误:

错误使用 mex
nvcc fatal   : Could not set up the environment for Microsoft Visual Studio using 'D:/Program Files (x86)/Microsoft Visual Studio
12.0/VC/Bin/amd64/../../../VC/bin/amd64/vcvars64.bat'
  • 1
  • 2
  • 3

如果你编译的时候还有错,注意具体问题具体分析了,一般是版本号的问题。

运行成功后的结果如下:

>> testMexCuda
>> y

y =

     2     2     2     2
     2     2     2     2
     2     2     2     2
     2     2     2     2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

3.总结


  1. 用mex调用CUDA是Matlab利用CUDA最简单的方式。在探索过程中,本人也看到了网上所说的用VS生成lib或dll的方法,经试验没有一个是成功的。自己做lib依赖项很难控制,mex的时候还是会产生一大堆LNK2001错误。
  2. mex调用CUDA的关键在于配置文件mex_CUDA_win64.xml。每次用mex编译时将修改好的xml文件放在当前目录下,那么编译.cu文件与编译.cpp文件操作完全相同。还有一种方法,将修改好的mex_CUDA_win64.xml复制到D:\Program Files\MATLAB\R2014b\bin\win64\mexopts,这样一劳永逸,每次mex编译时不必再将xml文件带着了。
  3. 解决了编译问题,剩下的就是如何利用好CUDA加速你的程序了。


阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭