Jacket for MATLAB
홈 > SW제품 > Jacket for MATLAB

NV-MEX 강좌

 Matlab에서 NV-MEX를 이용하면 CUDA와 Matlab을 연동할 수 있습니다.

 

MEX 구조

 

MEX는 Matlab EXecutable의 약자로, Matlab의 장점인 그래픽 제어 등의 편이성 및 벡터 관리의 장점과 C언어의 장점인 속도와 확장성을 동시에 충족시키는 유용한 툴이다.

 

MEX는 다음과 같이 입력파라미터 부분인 RHS(right hand side)과 결과파라미터를 출력하는 LHS(left hand side)과 함수명으로 이주어져 있다. 

 

> LHS = function (RHS)

 

실제 함수 선언은 다음과 같은 구조를 가지고 있는데,

void mexFunction(int nlhs, mxArray *plhs[],
                            int nrhs, const mxArray *prhs[]) {   } 

 

표준 C언어의 void main(int argc, char* argv[]){   } 와 유사한 구조임을 쉽게 알 수 있다.

 

MEX파일은 C/fortran 컴파일러를 연결해주는 perl script 구문과 Matlab 함수와의 연결관련 API를 제공하는 MEX header 및 라이브러리 파일로 구성되어 있다.  따라서, 사용자는 새로운 함수를 MEX API 규약에 맞게 함수를 정의하여 컴파일한 후 matlab에서 함수를 로드하여 사용할 수 있다. 

 

NV-MEX 구조

MEX는 gcc, lcc, cl 등의 컴파일러를 사용하는데, CUDA가속기능을 MEX를 이용하기 위해서는 CUDA 컴파일이 가능한 nvcc 컴파일러를 사용하여야 한다. 기존의 Matlab MEX에서는 nvcc 컴파일러를 지원하지 않으므로 CUDA관련 컴파일이 가능하도록 조정해주어야 한다. NVIDIA에서는 MEX perl 스크립트를 변형한 NV-MEX perl 스크립트를 제공하고 있으며, 이를 이용하여 nvcc와 Matlab의 연동이 가능하다.

 

세팅순서

NV-MEX의 환경 설정 순서는 다음과 같다.

1. Matlab 설치

2. (Windows의 경우) Visual Studio 2005/2008 설치

2. CUDA driver, toolkit, SDK 설치

3. NV-MEX 다운로드 및 설치

4. Matlab 데모예제 확인

5. (optional) Jacket 1.2 설치

6. Matlab + CUDA 개발

 

링크목록

http://www.mathworks.com/support/tech-notes/1600/1605.html : Matlab MEX 강좌

http://www.nvidia.com/object/matlab_acceleration.html : CUDA+Matlab 성공스토리

http://developer.nvidia.com/object/matlab_cuda.html : NVMEX 파일 다운로드

http://www.cs.ucf.edu/~janaka/gpu/using_nvmex.htm : NVMEX 파일 다운로드

 

 

다음은 NVMEX 샘플예제로, Matlab에서 CUDA를 적용시키는 방법을 확인할 수 있다.

 

#include "mex.h"

#include "cufft.h"

#include "cuda_runtime.h"

 

/**************************************************************************/

 

/* MATLAB stores complex numbers in separate arrays for the real and

   imaginary parts.  The following functions take the data in

   this format and pack it into a complex work array, or

   unpack it, respectively. 

   We are using cufftComplex defined in cufft.h to  handle complex on Windows and Linux

 

*/

 

void pack_r2c(cufftComplex  *output_float,

              double *input_re,

              int Ntot)

{

    int i;

    for (i = 0; i < Ntot; i++)

              {

               output_float[i].x = input_re[i];

               output_float[i].y = 0.0f;

              }

}

 

void pack_c2c(cufftComplex  *output_float,

              double *input_re,

              double *input_im,

              int Ntot)

{

    int i;

    for (i = 0; i < Ntot; i++)

             {

               output_float[i].x = input_re[i];

               output_float[i].y = input_im[i];

             }

}

 

 

void pack_r2c_expand(cufftComplex  *output_float,

                     double *input_re,

                     int originalM,

                     int originalN,

                     int M,

                     int N)

{

     int i, j;

    for (i = 0; i < originalM; i++)

     for (j = 0; j < originalN; j++)

     {

               output_float[i+M*j].x = input_re[i+originalM*j];

               output_float[i+M*j].y = 0.0f;

     }

}

 

void pack_c2c_expand(cufftComplex  *output_float,

                     double *input_re,

                     double *input_im,

                     int originalM,

                     int originalN,

                     int M,

                     int N)

{

     int i, j;

 

    for (i = 0; i < originalM; i++)

     for (j = 0; j < originalN; j++)

     {

               output_float[i+M*j].x = input_re[i+originalM*j];

               output_float[i+M*j].y = input_im[i+originalM*j];

     }

}

 

void unpack_c2c(cufftComplex  *input_float,

                double *output_re,

                double *output_im, 

                int Ntot)

{

    int i;

    for (i = 0; i < Ntot; i++)

    {

               output_re[i] = input_float[i].x;

               output_im[i] = input_float[i].y;

    }

 

}

 

 

/**************************************************************************/

 

void mexFunction( int nlhs, mxArray *plhs[],

                  int nrhs, const mxArray *prhs[])

{

  int originalM, originalN, M, N;

  double *ar,*ai;

  cufftComplex *input_single ;

  cufftHandle            plan;

  cufftComplex *rhs_complex_d;

 

  /*

     Find out the  dimension of the array we want to transform:

 

     prhs(originalM,originalN)

     originalM = Number of rows    in the mxArray prhs

     originalN = Number of columns in the mxArray prhs

 

  */

 

    originalM = mxGetM(prhs[0]);

    originalN = mxGetN(prhs[0]);

 

    M = originalM;

    N = originalN;

 

 

  /*

    Find out if the result is of the same size of the input.

 

    plhs(M,N)

    M = The number of rows in the mxArray plhs

    N = The number of columns in the mxArray plhs

 

  */

   if (nrhs == 3)

   {

    M = mxGetScalar(prhs[1]);

    N = mxGetScalar(prhs[2]);

   }

 

  

  /*

    Find out if the input array was real or complex.

    Matlab is passing two separate pointers for the real and imaginary part.

    The current version of  CUDAFFT is expecting interleaved data.

    We will need to pack and unpack the data.

 

    The current version of CUDA supports only single precision,

    so the original double precision data need to be converted.

  */

 

  /* Allocating working array on host */

    if(nrhs == 1) input_single  = (cufftComplex*) mxMalloc(sizeof(cufftComplex)*N*M);

    if(nrhs == 3) input_single  = (cufftComplex*) mxCalloc(N*M,sizeof(cufftComplex));

 

 

  /* Pointer for the real part of the input */

    ar =  (double *) mxGetData(prhs[0]);

 

  if(mxIsComplex(prhs[0]))

  {

   /* The input array is complex */

   ai =  (double *) mxGetImagData(prhs[0]);

  

   /* Input and output have same dimensions */

   if(nrhs ==1) pack_c2c(input_single, ar, ai, N*M);

 

   /* Input and output have different dimensions */

   if(nrhs ==3) pack_c2c_expand(input_single, ar, ai, originalM, originalN, M, N);

 

  }

  else

  {

   /* The input array is real */

 

   /* Input and output have same dimensions */

   if(nrhs ==1) pack_r2c(input_single, ar, N*M);

 

   /* Input and output have different dimensions */

   if(nrhs ==3) pack_r2c_expand(input_single, ar, originalM, originalN, M, N);

  }

 

  /* Allocate array on device */

  cudaMalloc( (void **) &rhs_complex_d,sizeof(cufftComplex)*N*M);

 

  /* Copy input array in interleaved format to the device */

  cudaMemcpy( rhs_complex_d, input_single, sizeof(cufftComplex)*N*M, cudaMemcpyHostToDevice);

 

 

  /* Create plan for CUDA FFT */

  cufftPlan2d(&plan, N, M, CUFFT_C2C) ;

 

  /* Execute FFT on device */

    cufftExecC2C(plan, rhs_complex_d, rhs_complex_d, CUFFT_FORWARD) ;

 

  /* Destroy plan */

    cufftDestroy(plan);

 

  /* Copy result back to host */

  cudaMemcpy( input_single, rhs_complex_d, sizeof(cufftComplex)*N*M, cudaMemcpyDeviceToHost);

 

 

  plhs[0]=mxCreateDoubleMatrix(M,N,mxCOMPLEX);

 

  ar = mxGetPr(plhs[0]);

  ai = mxGetPi(plhs[0]);

  unpack_c2c(input_single, ar, ai, N*M);

 

  mxFree(input_single);

  cudaFree(rhs_complex_d);

 

  return;

}