最近因为要做张量的模态积,所以要考虑使用cuda来进行并行的编程,但是c++实在太麻烦,尤其是在有MATLAB的时候,写c++简直就是一种“浪费时间”的行为。如果能用MATLAB调用cuda的程序那该是一件多么美好的事情呀。
确实,这件事情非常美好,但是配置开发环境的过程却是非常痛苦,我花了将近一个星期的时间才把这个问题解决,希望读者能在看完本文后节约宝贵的时间。
如果你用的是版本比较老的VS(比如2005)和matlab,那么这个问题其实很好办,只要调用nvmex函数就好了,但是据stackoverflow的网友说,自MATLAB2010a开始,nvmex就不被支持了,因此网上有很多答案讨论如何改nvmex函数使得能在更高版本的MATLAB运行,但我试了很多源码都不成功,于是乎放弃。好在MATLAB出了个比较新的MATLABR2015b的版本,这个版本有个mexcuda函数, 用起来相当舒心,只要一两句代码就能调用.cu程序,唯一的不足就在于传入的数组必须是GPUARRay的形式,而且重新下载MATLAB再安装也是挺不舒服的。另外一个要注意的就是在mathworks的releasenote里有说哪个版本的MATLAB支持哪个版本的cuda。所以重装什么的绝对不是一个最好的办法呀。下面就给出两个解决办法:
这个办法写得很完整也很细致,我所遇到的问题和他遇到的问题几乎是一样的,读者只要对照着去做就好,我亲自测试过,测试环境是MATLAB_R2014a+VS2010+cuda7.5。还有一点值得注意的是,木子超所给出的代码有的地方是有点瑕疵的,需要读者自己完善,这里我就不说了。以下给出链接:
http://blog.csdn.net/endlch/article/details/44561535
这个方法我也亲自测试过,测试环境同上,作者写得非常好,就是有个小细节要注意
COMPFLAGS="-gencode=arch=compute_20,code=sm_20 -gencode=arch=compute_30,code=sm_30 -gencode=arch=compute_50,code=\"sm_50,compute_50\" --compiler-options=/c,/GR,/W3,/EHs,/nologo,/MD"
在上面这个地方,如果你是cuda7.5的版本一定要把第一行第一个出现的arch和code后面的那个数字都改成20了(我记得cuda5.5这俩数字都是13),不然会出现nvcc报错,也就是我第一段说的MATLAB找不到它支持的编译器的架构的问题。以下是链接:
http://blog.csdn.net/hanlin_tan/article/details/48790273
%%%%%%%%%%%% 2018年更新 %%%%%%%%%%%%%%
上面讲述了如何在MATLAB2014下进行配置,时间来到了2018年,可能Mathwork公司已经意识到深度学习的火热和大家配置Cuda+MATLAB开发环境的痛苦,所以最新版本的MATLAB(据我所知是MATLAB2016a之后的版本)定义了一个新的语句:”mexcuda”。这个语句使用起来相当方便,比如你有一个向量相加的程序(VectorAdd.cu),你只需要按照编译C++ mex 文件的方法去编译就行了,直接敲击mexcuda VectorAdd.cu, 等待一会,就可以生成你想要的可执行文件了。注意MATLAB官方给了一个模板,但你其实不需要按照它给的函数传递方法去定义GPUArray,传统的mex方法依旧可行。为了方便大家的理解,我给出以下的一个例子:
#include "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v7.5\include\cuda_runtime.h"
#include "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v7.5\include\device_launch_parameters.h"
#include
#include
cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size);
__global__ void addKernel(int *c, const int *a, const int *b)
{
int i = threadIdx.x;
c[i] = a[i] + b[i];
}
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
// initialize
int *array_A = (int*)mxGetPr(prhs[0]);
int *array_b = (int*)mxGetPr(prhs[1]);
const int arraySize = 5;
int c[arraySize] = { 0 };
// Add vectors in parallel.
cudaError_t cudaStatus = addWithCuda(c, array_A, array_b, arraySize);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "addWithCuda failed!");
}
printf("{1,2,3,4,5} + {10,20,30,40,50} = {%d,%d,%d,%d,%d}\n",
c[0], c[1], c[2], c[3], c[4]);
// cudaDeviceReset must be called before exiting in order for profiling and
// tracing tools such as Nsight and Visual Profiler to show complete traces.
cudaStatus = cudaDeviceReset();
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaDeviceReset failed!");
}
}
// Helper function for using CUDA to add vectors in parallel.
cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size)
{
int *dev_a = 0;
int *dev_b = 0;
int *dev_c = 0;
cudaError_t cudaStatus;
// Choose which GPU to run on, change this on a multi-GPU system.
cudaStatus = cudaSetDevice(0);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaSetDevice failed! Do you have a CUDA-capable GPU installed?");
goto Error;
}
// Allocate GPU buffers for three vectors (two input, one output) .
cudaStatus = cudaMalloc((void**)&dev_c, size * sizeof(int));
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMalloc failed!");
goto Error;
}
cudaStatus = cudaMalloc((void**)&dev_a, size * sizeof(int));
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMalloc failed!");
goto Error;
}
cudaStatus = cudaMalloc((void**)&dev_b, size * sizeof(int));
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMalloc failed!");
goto Error;
}
// Copy input vectors from host memory to GPU buffers.
cudaStatus = cudaMemcpy(dev_a, a, size * sizeof(int), cudaMemcpyHostToDevice);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMemcpy failed!");
goto Error;
}
cudaStatus = cudaMemcpy(dev_b, b, size * sizeof(int), cudaMemcpyHostToDevice);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMemcpy failed!");
goto Error;
}
// Launch a kernel on the GPU with one thread for each element.
addKernel<<<1, size>>>(dev_c, dev_a, dev_b);
// Check for any errors launching the kernel
cudaStatus = cudaGetLastError();
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "addKernel launch failed: %s\n", cudaGetErrorString(cudaStatus));
goto Error;
}
// cudaDeviceSynchronize waits for the kernel to finish, and returns
// any errors encountered during the launch.
cudaStatus = cudaDeviceSynchronize();
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaDeviceSynchronize returned error code %d after launching addKernel!\n", cudaStatus);
goto Error;
}
// Copy output vector from GPU buffer to host memory.
cudaStatus = cudaMemcpy(c, dev_c, size * sizeof(int), cudaMemcpyDeviceToHost);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMemcpy failed!");
goto Error;
}
Error:
cudaFree(dev_c);
cudaFree(dev_a);
cudaFree(dev_b);
return cudaStatus;
}
注意开头的两行对应的是相应的Cuda安装目录,如果你懒得管,你在安装Cuda的时候只要一路点击确定就行了。
还有另外一个问题,相信大家都注意到了,那就是版本的问题,据我所知,Cuda已经出到了9.1,但是MATLAB还是有一定滞后的,但是我们也不能一辈子使用Cuda7.5和VS2012, 我上Mathwork的官方论坛和Stackoverflow搜索了以下,得到了以下网友的回复:
引用块内容
This is how to compile (mexcuda) with Visual Studio 2015 and Cuda 8.0 :
1:Go to: “\toolbox\distcomp\gpu\extern\src\mex\win64”
2:Copy files and rename 2013 to 2015: { “nvcc_msvcpp2013.xml” , “nvcc_msvcpp2013_dynamic.xml”}
3:Replace inside those files “7.5” to “8.0” and “12” to “14”.
4:Done.
我今天实验以下,如果成功了,我再来更新。
[1]: http://blog.csdn.net/endlch/article/details/44561535
[2]: http://blog.csdn.net/hanlin_tan/article/details/48790273