通过CUDA加速Matlab优化

时间:2013-07-05 16:48:54

标签: matlab optimization cuda numerical

我想知道我是否可以通过简单地将循环分解为GPU线程来提高我在Matlab中使用CUDA进行模拟的速度。

到目前为止,我已经在R中使用mclapply完成了循环,但我想知道,如果我能够使用CUDA 在Matlab中加速它? (Nvidia Gtx 650 ti boost)

以下是我正在做的简化示例: 我有一个dim 2000x9的数据集,我想运行至少250个带有移动数据窗口的循环(大约1500行),这些循环是独立的,因此非常适合并行计算。

循环示例:取前1500行。整个数据集(1500x9)的一些魔力。计算每列的单变量函数(在1500x1上进行9次运算)然后运行最小化问题,每列有一定的损失函数(在1500x1上进行9次运算)。 (在我之间我也在处理所有列,所以我不能在不同的线程中分隔列)

我的想法: 如果我在不同的GPU线程中运行每个循环?在单GPU核心中做这么难的计算问题是否有意义? (例如,1个内核上的i7 3770k上的1个循环大约需要300秒)

我知道单个GPU线程比CPU慢得多,但是如果所有循环都同时运行?特别是因为每个循环在给定数据时需要很长时间,因此将数据提供给GPU将只占整个时间的不可忽视的一部分。

重要提示:我是一个非常糟糕的“程序员”,我计划的只是在变量i的函数中将for循环(i在1:250中)拆分并将其传递给GPU。

2 个答案:

答案 0 :(得分:2)

我看到三个主要问题与每个线程应该执行整个最小化过程这一事实有关(您目前正在使用哪种Matlab最小化例程?fminuncfminsearchminFunc ?)。

  1. 在需要的临时变量方面,最小化可能很苛刻。这可能会限制算法的性能,因为需要存储临时数据并进行交易,例如,使用全局内存,当然这取决于您实现它的意义。

  2. 您还应该仔细考虑线程同步,因为完成最小化过程所需的时间可能会因线程而异。

  3. Matlab具有非常有效的优化例程,其性能通常很难(但当然,并非不可能)通过自定义实现进行复制。根据我的经验,Matlab的fminunc比NAG提供的Broyden-Fletcher-Goldfarb-Shanno等效例程更有效。因此,如果您尝试翻译上述优化例程之一,那么最终可能会得到一个不太令人满意的结果。

  4. 我使用Matlab加速CUDA遇到了许多优化问题,我的“黄金法则”是使用Matlab的一个优化例程,并通过特意编写加速直接问题(函数的计算)和函数渐变的解决方案CUDA代码通过mex文件与Matlab连接。考虑到特别是梯度需要(并且可以)加速,因为通过有限差分计算函数导数是独立的,并且需要调用与优化参数的数量一样多的函数计算例程。

    修改 假设我必须优化目标函数objfun。我正在做的是使用mex文件接口在CUDA中编写objfun代码,用nvcc编译它,然后在Matlab下链接它。

    当我使用Matlab 2010时,CUDA函数由nvcc编译,并通过命令转换为C ++代码

    system(sprintf('nvcc -I"%s/extern/include" --cuda "mexfun.cu" --output-file "mexfun.cpp"', matlabroot));
    

    然后通过

    链接到Matlab
    mex -I/opt/cuda/include -L/opt/cuda/lib -lcudart mexfun.cpp
    

    ,如Compiling CUDA C/C++ mex code under linux中所述。

    然后,当使用例如fminunc(@mexfun,...)时,Matlab将优化目标功能,并且将在GPU上执行(并因此加速)对其的每个评估。当分析可用时,我也使用相同的方法对梯度计算进行编码,因为用于评估梯度的有限差异会显着减慢整个优化过程。

    对于Matlab 2013和Windows系统,请参阅Creating mex files from CUDA code

    编辑 mexfun.cu(目标函数)的结构

    // Do not change the function name (`mexFunction`) and the function arguments (`nlhs`, `plhs`, ...). 
    void mexFunction(int nlhs, mxArray *plhs[],int nrhs, const mxArray *prhs[])
    
    {
        /* Maps Matlab's pointers to the input variables to CUDA pointers */
        double* input_1     = mxGetPr(prhs[0]);
        double* input_2     = mxGetPr(prhs[1]);
    
        /* Recovers the size of the input matrices */
        int dimx = mxGetN(prhs[0]);
        ...         
        int dimu = mxGetM(prhs[3]);         
    
        /* Memory allocations on the host */
        cuDoubleComplex* hfoo = (cuDoubleComplex *)malloc(sizeof(cuDoubleComplex)*dimx);
        ...
    
       /* Memory allocations on the device */
       cuDoubleComplex* dfoo; cudaMalloc((void*)&d_Kernel_Matrix,dimx*sizeof(cuDoubleComplex));
       ...
    
      /* Memory transfer from host to device */
      cudaMemcpy(dfoo,hfoo,dimx*sizeof(cuDoubleComplex),cudaMemcpyHostToDevice);
      ....
    
      /* Kernel launch */
      dim3 dimBlock(BLOCK_SIZE_X,BLOCK_SIZE_Y);
      Kernel_To_Be_Launched <<<dimGrid,dimBlock >>>(hfoo,dfoo,dimx);
    
     /* Copy the results from device to host */ cudaMemcpy(hfoo,dfoo,dimx*sizeof(cuDoubleComplex),cudaMemcpyDeviceToHost);
    
    
     /* Passing the output matrices to MATLAB */
     plhs[0] = mxCreateDoubleMatrix(1,dimu,mxCOMPLEX);
     double* hfoo_re = mxGetPr(plhs[0]);
     double* hfoo_im = mxGetPi(plhs[0]);
    
     /* Freeing host memory */
     free(hfoo);
     ...
    
     /* Freeing device memory */
     cudaFree(dfoo);
    

    }

答案 1 :(得分:0)

我不认为自己是CUDA的专家(根本没有),但过去我一直在广泛使用它。我的猜测是,虽然你可能确实得到一些加速,但如果没有详细的问题知识,只有你拥有,并且可能没有一些努力,很难说多少。也就是说,你可能不能只是“把它扔到墙上”,可以这么说,并希望CUDA编译器能抓住所有的碎片。

我的直接关注点与内存管理和总线流量有关,因为CUDA对内存使用有非常严格的规则。虽然编译器通常会尽可能地保持正常运行,但如果您使用内存和总线效率低下,性能降级。

具体来说,为了获得良好的性能,您希望将问题的各个部分加载到各种流式多处理器的共享内存中。现代卡上SM的可用共享内存仅为48K。你用1500 x 9(浮动,我假设)的块来描述你的问题已经超过了48K。此外,SM上的共享内存由SM上的所有处理器使用。如果您的问题占用了所有48K的SM,那么大部分SM将处于空闲状态。

听起来不错。但是,如果有一种方法可以计算出较小块中的1500 x 9块的答案并重新组合,那么您可能有GPU方法的候选者。通常需要一些创造力。

但我强调,这只是一个问题。这是一个跳出来的人,因为它类似于我正在为另一个应用程序摔跤的问题。

JackOLantern提出了其他人,还有读/写模式等。