Skip navigation

Saját C++ kód

C++ program MATLAB-ban?

Elérkezhet olyan helyzet, amikor olyan műveletre van szükségünk a GPU-n, ami nincs benne a MATLAB alap funkciókészletében. Ilyenkor saját GPU kódot kellene írnunk. Ehhez viszont a MATLAB-nak nincs saját eszköztára.

Van viszont egy MEX nevű kiterjesztés, amivel külső kódokat tudunk befordítani a MATLAB-ba. Így például CUDA és C++ kódokat.

Az egyszerűség kedvéért először kezdjünk az egyszerű C++ kódok integrálásával. Utána majd foglalkozunk a CUDA-val is.

A feladat egyszerűen két mátrix összeadása lesz. A lenti példa egy cpp forráskódból áll, ami teljes egészében letölthető innen (vagy egy megtalálható az oldal alján)illetve az azt kezelő MATLAB kódból.

C++ kód

A MEX függvények írásához egy egyszerű c++ kódot írunk .cpp kiterjesztésű fileba.

A MATLAB-ból hívott függvénynek fix fejléce van, ami valahogy így néz ki:

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

A függvény fejléce - bár érdekes betűkombinációkat tartalmaz - nem túl bonyolult. Konkrétan négy vátozót kapunk a MATLAB-bül amiket kezelnünk kell.

  • nlhs: A bemeneti paraméterek száma;
  • plhs: Pointer a bementei adatokat leíró tömbre. Minden adathoz kapunk egy mxArray típusú struktúrát, amiből kiolvashatjuk, hogy milyen és mekkora tömböket kaptunk (ugye a MATLAB-ban, minden tömb), és hogy azok tartalma hol van a memóriában.
  • nrhs: A kimenetek száma;
  • prhs: Pointer a kimenetek tömbjére. Ugyanúgy működik mint a bemeneteknél.

Tehát gyakorlatilag megkapjuk egy tömbben a bemeneteket, és egy tömbben a kimeneteket, amiknek kezeléséhez találunk függvényeket a mex.h függvénykönyvtárban. A programozás innentől kezdve csak az adatok kezelésében új.

Először érdemes leellenőriznünk, hogy jó adatokat kaptunk-e. Ha nem, akkor adunk egy hibaüzenetet.

    if(nrhs != 2) {
        mexErrMsgIdAndTxt("MyToolbox:arrayProduct:nrhs",
                          "Two inputs required.");
    }

    if(nlhs != 1) {
        mexErrMsgIdAndTxt("MyToolbox:arrayProduct:nlhs",
                          "One output required.");
    }
    
    if( !mxIsDouble(prhs[0]) || 
         mxIsComplex(prhs[0])) {
        mexErrMsgIdAndTxt("MyToolbox:arrayProduct:notDouble",
                          "Input multiplier must be type double.");
    }

    if( !mxIsDouble(prhs[1]) || 
         mxIsComplex(prhs[1])) {
        mexErrMsgIdAndTxt("MyToolbox:arrayProduct:notDouble",
                          "Input matrix must be type double.");
    }
    
    if(mxGetM(prhs[0]) != 1) {
        mexErrMsgIdAndTxt("MyToolbox:arrayProduct:notRowVector",
                          "First input must be a row vector.");
    }
    
    if(mxGetM(prhs[1]) != 1) {
        mexErrMsgIdAndTxt("MyToolbox:arrayProduct:notRowVector",
                          "Second input must be a row vector.");
    }
    
    if(mxGetN(prhs[0]) != mxGetN(prhs[1])) {
        mexErrMsgIdAndTxt("MyToolbox:arrayProduct:notRowVector",
                          "Two input must have the same size.");
    }

Ha minden rendben volt, akkor rátérhetünk a programra. A Matlab api-n k eresztül elkérhetjük a bemeneti paraméterek adataira mutató pointereket.

    double *inVector1 = mxGetPr(prhs[0]);
    double *inVector2 = mxGetPr(prhs[1]);

Ugyancsak lekérdezhetjük a mátrixok elemszámát

    mwSize N;
    
    N = mxGetN(prhs[0]);

Elkészíthetjük az eredményeknek szánt kimeneti tömböt.

    double *outVector;

    N = mxGetN(prhs[0]);
    
    plhs[0] = mxCreateDoubleMatrix(1,N,mxREAL);
    outVector = mxGetPr(plhs[0]);

És végül elindíthatjuk a számítász, amit ebben az eserben az arrayAdd(...) függvényben valósítottunk meg.

arrayAdd(inVector1,inVector2,outVector,N);

A függvény egyszerű elemenkénti összeadást valósít meg. Valójában még arról sem tud, hogy mátrixokat ad össze. A bemenetek tömbjeiben az adatok egymás mögé vannak felfízve, így feldolgozhatóak vektor-ként is.

void arrayAdd(double *x, double *y, double *z, mwSize n)
{
    mwSize i;
  
    for (i=0; i<n; i++) {
        z[i] = x[i] + y[i];
    }
}

MATLAB keret

Ha megvan a megfelelő kódunk C++ forrása, akkor a matlab-beli felhasználása nem bonyolult.

Először le kell fordítanunk a kódot:

mex mexAdd.cpp

Majd adatokat definiálunk, és úgy használjuk a függvényünket, mint minden más MATLAB függvényt.

A = [1, 2, 3, 4];
B = [5, 5, 5, 5];

C = mexAdd(A, B)

Teljes C++ kód

#include "mex.h"

void arrayAdd(double *x, double *y, double *z, mwSize n)
{
  mwSize i;
  
  for (i=0; i<n; i++) {
    z[i] = x[i] + y[i];
  }
}

/* The gateway function */
void mexFunction(int nlhs, mxArray *plhs[],
                 int nrhs, const mxArray *prhs[])
{
    if(nrhs != 2) {
        mexErrMsgIdAndTxt("MyToolbox:arrayProduct:nrhs",
                          "Two inputs required.");
    }

    if(nlhs != 1) {
        mexErrMsgIdAndTxt("MyToolbox:arrayProduct:nlhs",
                          "One output required.");
    }
    
    if( !mxIsDouble(prhs[0]) || 
         mxIsComplex(prhs[0])) {
        mexErrMsgIdAndTxt("MyToolbox:arrayProduct:notDouble",
                          "Input multiplier must be type double.");
    }

    if( !mxIsDouble(prhs[1]) || 
         mxIsComplex(prhs[1])) {
        mexErrMsgIdAndTxt("MyToolbox:arrayProduct:notDouble",
                          "Input matrix must be type double.");
    }
    
    if(mxGetM(prhs[0]) != 1) {
        mexErrMsgIdAndTxt("MyToolbox:arrayProduct:notRowVector",
                          "First input must be a row vector.");
    }
    
    if(mxGetM(prhs[1]) != 1) {
        mexErrMsgIdAndTxt("MyToolbox:arrayProduct:notRowVector",
                          "Second input must be a row vector.");
    }
    
    if(mxGetN(prhs[0]) != mxGetN(prhs[1])) {
        mexErrMsgIdAndTxt("MyToolbox:arrayProduct:notRowVector",
                          "Two input must have the same size.");
    }
    
    double *inVector1 = mxGetPr(prhs[0]);
    double *inVector2 = mxGetPr(prhs[1]);
    
    mwSize N;
    
    double *outVector;

    N = mxGetN(prhs[0]);
    
    plhs[0] = mxCreateDoubleMatrix(1,N,mxREAL);
    outVector = mxGetPr(plhs[0]);
    
    arrayAdd(inVector1,inVector2,outVector,N);

}