Reducing run time of a numerical calculation using a mex file in Matlab
3 views (last 30 days)
Show older comments
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?
0 Comments
Answers (1)
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
See Also
Categories
Find more on Write C Functions Callable from MATLAB (MEX Files) in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!