Reducing run time of a numerical calculation using a mex file in Matlab

3 views (last 30 days)
I wrote a Matlab code that involves doing a numeric calculation (relaxation), but it is quite slow. I learned of the possibility of using a mex file to run a C code and integrate it into Matlab, so I was thinking of doing the numerical calculation (which is relatively simple but involves loops and takes time) in C, and the rest (before and after) in Matlab.
The part of my Matlab code where the calculation is done:
% evolution of the potentials %
% note : for the index directions with periodic boundary conditions: index=mod(index-1,L)+1 . for index=index+1 it is mod(index,L)+1 , and for index=index-1 it is mod(index-2,L)+1 %
for i_t=1:max_relaxation_iterations
for q=1:length(i_eff_V_bounded) % this is set instead of running i=2:(L-1), j=1:L , k=1:L and ending up going over sites that are 0 in our effective system %
i=i_eff_V_bounded(q);
j=j_eff_V_bounded(q);
k=k_eff_V_bounded(q);
V0=V(i,j,k);
V1=( V(i+1,j,k)+V(i-1,j,k)+V(i,mod(j,L)+1,k)+V(i,mod(j-2,L)+1,k)+V(i,j,mod(k,L)+1)+V(i,j,mod(k-2,L)+1) )/( system(i+1,j,k)+system(i-1,j,k)+system(i,mod(j,L)+1,k)+system(i,mod(j-2,L)+1,k)+system(i,j,mod(k,L)+1)+system(i,j,mod(k-2,L)+1) ); % evolving the potential as the average of its occupied neighbors %
V(i,j,k)=V0+(V1-V0)*over_relaxation_factor; % evolving the potentials in time with the over relaxation factor %
delta_V_rms(i_t)=delta_V_rms(i_t)+(V1-V0)^2; % for each t at a given p, we sum over (V1-V0)^2 in order to eventually calculate delta_V_rms_avg %
delta_V_abs(i_t)=delta_V_abs(i_t)+abs(V1-V0); % for each t at a given p, we sum over |V1-V0| in order to eventually calculate delta_V_abs_avg %
delta_V_max(i_t)=max(abs(V1-V0),delta_V_max(i_t)); % for each t at a given p, we take the max of |V1-V0| from all the sites in order to eventually calculate delta_V_max_avg %
end
end
So in C it should be something like:
#include <stdio.h>
int mod(int x,int N) /* a function for the modulo operator (instead of the remainder operator which is the % operator) assuming N is positive (x can be negative) */
{
return (x%N+N)%N;
}
double d_abs(double x) /* a function for the absolute value operator */
{
if x<0
{
return -x;
}
else
{
return x;
}
}
double max(double x,double y) /* a function for the max operator */
{
if x>y
{
return x;
}
else
{
return y;
}
}
/* evolution of the potentials */
/* note : periodic boundary conditions for the j,k directions */
void potentials_evolution(int max_relax_iters,int N_eff_occ_sites,int i_eff_V_bounded[],int j_eff_V_bounded[],int k_eff_V_bounded[],int system[][][],over_relax_fact,double V[][][],double delta_V_rms[],double delta_V_abs[],double delta_V_max[])
{
int i_t,q,i,j,k;
double V0,V1;
for(i_t=0;i_t<max_relax_iters;i_t++)
{
for(q=0;q<N_eff_occ_sites;q++) /* going over only the occupied sites left in our effective system */
{
i=i_eff_V_bounded[q];
j=j_eff_V_bounded[q];
k=k_eff_V_bounded[q];
V0=V[i][j][k];
V1=( V[i+1][j][k]+V[i-1][j][k]+V[i][mod(j+1,L)][k]+V[i][mod(j-1,L)][k]+V[i][j][mod(k+1,L)]+V[i][j][mod(k-1,L)] )/( system[i+1][j][k]+system[i-1][j][k]+system[i][mod(j+1,L)][k]+system[i][mod(j-1,L)][k]+system[i][j][mod(k+1,L)]+system[i][j][mod(k-1,L)] ) /* evolving the potential as the average of its occupied neighbors */
V[i][j][k]=V0+(V1-V0)*over_relax_fact; /* evolving the potentials in time with the over relaxation factor */
delta_V_rms[i_t]=delta_V_rms[i_t]+(V1-V0)*(V1-V0); /* for each t at a given p, we sum over (V1-V0)^2 in order to eventually calculate delta_V_rms_avg */
delta_V_abs[i_t]=delta_V_abs[i_t]+d_abs(V1-V0); /* for each t at a given p, we sum over |V1-V0| in order to eventually calculate delta_V_abs_avg */
delta_V_max[i_t]=max(d_abs(V1-V0),delta_V_max[i_t]); /* for each t at a given p, we take the max of |V1-V0| from all the sites in order to eventually calculate delta_V_max_avg */
}
}
}
And so in Matlab I will replace the part of my Matlab code shown above with something like:
potentials_evolution(max_relax_iters,N_eff_occ_sites,i_eff_V_bounded,j_eff_V_bounded,k_eff_V_bounded,system,over_relax_fact,V,delta_V_rms,delta_V_abs,delta_V_max);
How do I implement this? I tried looking around (in places such as here: https://www.mathworks.com/help/matlab/matlab_external/standalone-example.html) for a simple way to do it but I couldn't figure out how to properly do it.
Note 1: This numeric calculation is done not just once but many times for different systems that are generated randomly (there is a `for` loop going over the different systems).
Note 2: I've never used mex files and my C is quite rusty.
EDIT: After hours of trying to figure how to build the mex file, I came up with this:
#include "mex.h"
mwSize mod(mwSize x,mwSize N) /* a function for the modulo operator (instead of the remainder operator which is the % operator) assuming N is positive (x can be negative) */
{
return (x%N+N)%N;
}
double d_abs(double x) /* a function for the absolute value operator */
{
if(x<0)
{
return -x;
}
else
{
return x;
}
}
double max(double x,double y) /* a function for the max operator */
{
if(x>y)
{
return x;
}
else
{
return y;
}
}
/* in matlab i will invoke potentials_evolution(max_relax_iters,N_eff_occ_sites,i_eff_V_bounded,j_eff_V_bounded,k_eff_V_bounded,system,over_relax_fact,V,delta_V_rms,delta_V_abs,delta_V_max); */
/* evolution of the potentials */
/* note : periodic boundary conditions for the j,k directions */
void potentials_evolution(mwSize L,mwSize max_relax_iters,mwSize N_eff_occ_sites,mwSize *i_eff_V_bounded,mwSize *j_eff_V_bounded,mwSize *k_eff_V_bounded,mwSize ***system,double over_relax_fact,double ***V,double *delta_V_rms,double *delta_V_abs,double *delta_V_max)
{
int i_t,q,i,j,k;
double V0,V1;
for(i_t=0;i_t<max_relax_iters;i_t++)
{
for(q=0;q<N_eff_occ_sites;q++) /* going over only the occupied sites left in our effective system */
{
i=i_eff_V_bounded[q];
j=j_eff_V_bounded[q];
k=k_eff_V_bounded[q];
V0=V[i][j][k];
V1=( V[i+1][j][k]+V[i-1][j][k]+V[i][mod(j+1,L)][k]+V[i][mod(j-1,L)][k]+V[i][j][mod(k+1,L)]+V[i][j][mod(k-1,L)] )/( system[i+1][j][k]+system[i-1][j][k]+system[i][mod(j+1,L)][k]+system[i][mod(j-1,L)][k]+system[i][j][mod(k+1,L)]+system[i][j][mod(k-1,L)] ) /* evolving the potential as the average of its occupied neighbors */
V[i][j][k]=V0+(V1-V0)*over_relax_fact; /* evolving the potentials in time with the over relaxation factor */
delta_V_rms[i_t]=delta_V_rms[i_t]+(V1-V0)*(V1-V0); /* for each t at a given p, we sum over (V1-V0)^2 in order to eventually calculate delta_V_rms_avg */
delta_V_abs[i_t]=delta_V_abs[i_t]+d_abs(V1-V0); /* for each t at a given p, we sum over |V1-V0| in order to eventually calculate delta_V_abs_avg */
delta_V_max[i_t]=max(d_abs(V1-V0),delta_V_max[i_t]); /* for each t at a given p, we take the max of |V1-V0| from all the sites in order to eventually calculate delta_V_max_avg */
}
}
}
/* in matlab: [V,delta_V_rms,delta_V_abs,delta_V_max] = potentials_evolution(i_eff_V_bounded,j_eff_V_bounded,k_eff_V_bounded,system,over_relax_fact,V_0,delta_V_rms_0,delta_V_abs_0,delta_V_max_0) */
void mexFunction(int nlhs,mxArray *plhs,int nrhs,const mxArray *prhs[])
{
size_t L, *dim_V, max_relax_iters, N_eff_occ_sites;
size_t *i_eff_V_bounded, *j_eff_V_bounded, *k_eff_V_bounded;
size_t ***system;
double over_relax_fact;
double ***V, ***V_0;
double *delta_V_rms, *delta_V_abs, *delta_V_max, *delta_V_rms_0, *delta_V_abs_0, *delta_V_max_0;
/* additional input for the calculation */
dim_V = mxGetDimensions(prhs[5]);
L = dim_V[0];
max_relax_iters = mxGetN(prhs[6]);
N_eff_occ_sites = mxGetN(prhs[0]);
/* input arguments */
i_eff_V_bounded = mxGetInt32s(prhs[0]);
j_eff_V_bounded = mxGetInt32s(prhs[1]);
k_eff_V_bounded = mxGetInt32s(prhs[2]);
system = mxGetInt32s(prhs[3]);
over_relax_fact = mxGetScalar(prhs[4]);
V_0 = mxGetDoubles(prhs[5]);
delta_V_rms_0 = mxGetDoubles(prhs[6]);
delta_V_abs_0 = mxGetDoubles(prhs[7]);
delta_V_max_0 = mxGetDoubles(prhs[8]);
/* output arguments */
plhs[0] = mxCreateNumericArray(mxGetNumberOfDimensions(prhs[5]),mxGetDimensions(prhs[5]),mxDOUBLE_CLASS,mxREAL);
V = mxGetDoubles(plhs[0]);
plhs[1] = mxCreateNumericMatrix(max_relax_iters,1,mxDOUBLE_CLASS,mxREAL);
delta_V_rms = mxGetDoubles(plhs[1]);
plhs[2] = mxCreateNumericMatrix(max_relax_iters,1,mxDOUBLE_CLASS,mxREAL);
delta_V_abs = mxGetDoubles(plhs[2]);
plhs[3] = mxCreateNumericMatrix(max_relax_iters,1,mxDOUBLE_CLASS,mxREAL);
delta_V_max = mxGetDoubles(plhs[3]);
/* performing the calculation */
potentials_evolution((mwSize)L,(mwSize)max_relax_iters,(mwSize)N_eff_occ_sites,(mwSize)i_eff_V_bounded,(mwSize)j_eff_V_bounded,(mwSize)k_eff_V_bounded,(mwSize)system,over_relax_fact,V,delta_V_rms,delta_V_abs,delta_V_max);
}
Is this correct syntax/form-wise and does this match my Matlab code (shown above)? Do I need something else for it to run?

Answers (1)

Jorik
Jorik on 10 Aug 2019
If you have access to MATLAB Coder, it can let you generate a C-MEX implementation for your MATLAB code. If you use the Coder App, it will guide you through the steps to generate a MEX-function and validate it runs correctly.
If you'd like to write it manually, you'll need to learn the basics of the MEX API. I would start by copying and modifying a simple function like timestwo.c:
edit(fullfile(matlabroot, 'extern', 'examples', 'refbook', 'timestwo.c'))
You can lookup the API functions in the documentation to better understand the MEX and MATRIX API, e.g.:
and
  9 Comments
tensorisation
tensorisation on 17 Aug 2019
Edited: tensorisation on 17 Aug 2019
I've been away for a few days.
(1) in regards to 2.) : I looked in the example, so you can only use logical arrays for the output?
I should just turn my logical array to an integer class in matlab beforehand then for it to work properly, right? But for other arrays that are just default in Matlab I don't have to convert into a specific class beforehand? (for example: a=[1,5,7] in Matlab, needs to be converted to an integer beforehand?)
(2) In regards to 3.) : Oh, you mean it's *plhs instead of *plhs[]? I fixed it now.
(3) I went by the examples but I'm still not entirely sure why it uses mwSize and size_t instead of just int, can you explain this?
I now have:
#include "mex.h"
mwSize mod(mwSize x,mwSize N) /* a function for the modulo operator (instead of the remainder operator which is the % operator) assuming N is positive (x can be negative) */
{
return (x%N+N)%N;
}
double d_abs(double x) /* a function for the absolute value operator */
{
if(x<0)
{
return -x;
}
else
{
return x;
}
}
double max(double x,double y) /* a function for the max operator */
{
if(x>y)
{
return x;
}
else
{
return y;
}
}
/* in matlab i will invoke potentials_evolution(max_relax_iters,N_eff_occ_sites,i_eff_V_bounded,j_eff_V_bounded,k_eff_V_bounded,system,over_relax_fact,V,delta_V_rms,delta_V_abs,delta_V_max); */
/* evolution of the potentials */
/* note : periodic boundary conditions for the j,k directions */
void potentials_evolution(mwSize L,mwSize max_relax_iters,mwSize N_eff_occ_sites,mwSize *i_eff_V_bounded,mwSize *j_eff_V_bounded,mwSize *k_eff_V_bounded,mwSize ***system,double over_relax_fact,double ***V,double *delta_V_rms,double *delta_V_abs,double *delta_V_max)
{
int i_t,q,i,j,k;
double V0,V1;
for(i_t=0;i_t<max_relax_iters;i_t++)
{
for(q=0;q<N_eff_occ_sites;q++) /* going over only the occupied sites left in our effective system */
{
i=i_eff_V_bounded[q];
j=j_eff_V_bounded[q];
k=k_eff_V_bounded[q];
V0=V[i][j][k];
V1=( V[i+1][j][k]+V[i-1][j][k]+V[i][mod(j+1,L)][k]+V[i][mod(j-1,L)][k]+V[i][j][mod(k+1,L)]+V[i][j][mod(k-1,L)] )/( system[i+1][j][k]+system[i-1][j][k]+system[i][mod(j+1,L)][k]+system[i][mod(j-1,L)][k]+system[i][j][mod(k+1,L)]+system[i][j][mod(k-1,L)] ) /* evolving the potential as the average of its occupied neighbors */
V[i][j][k]=V0+(V1-V0)*over_relax_fact; /* evolving the potentials in time with the over relaxation factor */
delta_V_rms[i_t]=delta_V_rms[i_t]+(V1-V0)*(V1-V0); /* for each t at a given p, we sum over (V1-V0)^2 in order to eventually calculate delta_V_rms_avg */
delta_V_abs[i_t]=delta_V_abs[i_t]+d_abs(V1-V0); /* for each t at a given p, we sum over |V1-V0| in order to eventually calculate delta_V_abs_avg */
delta_V_max[i_t]=max(d_abs(V1-V0),delta_V_max[i_t]); /* for each t at a given p, we take the max of |V1-V0| from all the sites in order to eventually calculate delta_V_max_avg */
}
}
}
/* in matlab: [V,delta_V_rms,delta_V_abs,delta_V_max] = potentials_evolution(i_eff_V_bounded,j_eff_V_bounded,k_eff_V_bounded,system,over_relax_fact,V_0,delta_V_rms_0,delta_V_abs_0,delta_V_max_0) */
void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
{
size_t L, max_relax_iters, N_eff_occ_sites;
size_t *i_eff_V_bounded, *j_eff_V_bounded, *k_eff_V_bounded;
size_t ***system;
double over_relax_fact;
double ***V, ***V_0;
double *delta_V_rms, *delta_V_abs, *delta_V_max, *delta_V_rms_0, *delta_V_abs_0, *delta_V_max_0;
/* additional input for the calculation */
L = mxGetDimensions(prhs[5])[0];
max_relax_iters = mxGetN(prhs[6]);
N_eff_occ_sites = mxGetN(prhs[0]);
/* input arguments */
i_eff_V_bounded = mxGetInt32s(prhs[0]);
j_eff_V_bounded = mxGetInt32s(prhs[1]);
k_eff_V_bounded = mxGetInt32s(prhs[2]);
system = mxGetInt32s(prhs[3]);
over_relax_fact = mxGetScalar(prhs[4]);
V_0 = mxGetDoubles(prhs[5]);
delta_V_rms_0 = mxGetDoubles(prhs[6]);
delta_V_abs_0 = mxGetDoubles(prhs[7]);
delta_V_max_0 = mxGetDoubles(prhs[8]);
/* output arguments */
plhs[0] = mxCreateNumericArray(mxGetNumberOfDimensions(prhs[5]),mxGetDimensions(prhs[5]),mxDOUBLE_CLASS,mxREAL);
V = mxGetDoubles(plhs[0]);
plhs[1] = mxCreateNumericMatrix(max_relax_iters,1,mxDOUBLE_CLASS,mxREAL);
delta_V_rms = mxGetDoubles(plhs[1]);
plhs[2] = mxCreateNumericMatrix(max_relax_iters,1,mxDOUBLE_CLASS,mxREAL);
delta_V_abs = mxGetDoubles(plhs[2]);
plhs[3] = mxCreateNumericMatrix(max_relax_iters,1,mxDOUBLE_CLASS,mxREAL);
delta_V_max = mxGetDoubles(plhs[3]);
/* performing the calculation */
potentials_evolution((mwSize)L,(mwSize)max_relax_iters,(mwSize)N_eff_occ_sites,(mwSize)i_eff_V_bounded,(mwSize)j_eff_V_bounded,(mwSize)k_eff_V_bounded,(mwSize)system,over_relax_fact,V,delta_V_rms,delta_V_abs,delta_V_max);
}

Sign in to comment.

Categories

Find more on Write C Functions Callable from MATLAB (MEX Files) in Help Center and File Exchange

Products


Release

R2018b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!