0

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

到目前为止,我已经在 R 中使用 mclapply 完成了循环,但我想知道是否可以使用 CUDA 在 Matlab 中加速它?(Nvidia Gtx 650 ti 提升)

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

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

我的想法: 如果我将在不同的 GPU 线程中运行每个循环?在单个 GPU 内核中解决如此困难的计算问题是否有意义?(为了说明,1 个循环在 i7 3770k 上 1 个核心上大约需要 300 秒)

我知道与 CPU 相比,单个 GPU 线程要慢得多,但如果所有循环都将同时运行?尤其是由于每个循环在给定数据后需要很长时间,因此将数据提供给 GPU 将只占用总时间的一小部分。

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

4

2 回答 2

2

我看到三个主要问题与每个线程应该执行整个最小化过程(您当前使用哪种 Matlab 最小化例程fminunc????)fminsearchminFunc

  1. 就所需的临时变量而言,最小化可能要求很高。这可能会限制您的算法的性能,因为需要存储临时数据并处理它们,例如使用全局内存,这当然取决于您打算如何实现它。

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

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

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

编辑 假设我必须优化目标函数objfun。我正在做的是objfun在 CUDA 中使用 mex 文件接口编写代码,通过编译它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

正如在 linux 下编译 CUDA C/C++ mex 代码中所建议的那样。

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

对于 Matlab 2013 和 Windows 系统,请参阅从 CUDA 代码创建 mex 文件

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);

}

于 2013-07-06T20:55:34.433 回答
0

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

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

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

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

但我强调,这只是一个问题。 这是一个让我大吃一惊的问题,因为它与我正在为另一个应用程序挣扎的问题相似。

JackOLantern 养了别人,还有读/写模式等等。

于 2013-07-07T19:54:02.830 回答