Using Dynamic Memory Allocation for an Atoms Simulation
This example shows how to generate code for a MATLAB® algorithm that runs a simulation of bouncing "atoms" and returns the result after a number of iterations. There are no upper bounds on the number of atoms that the algorithm accepts, so this example takes advantage of dynamic memory allocation.
Prerequisites
There are no prerequisites for this example.
About the run_atoms
Function
The run_atoms.m
function runs a simulation of bouncing atoms (also applying gravity and energy loss).
help run_atoms
atoms = run_atoms(atoms,n) atoms = run_atoms(atoms,n,iter) Where 'atoms' the initial and final state of atoms (can be empty) 'n' is the number of atoms to simulate. 'iter' is the number of iterations for the simulation (if omitted it is defaulted to 3000 iterations.)
Set Up Code Generation Options
Create a code generation configuration object
cfg = coder.config;
Enable dynamic memory allocation for all variable-size arrays.
cfg.DynamicMemoryAllocationThreshold = 0;
Set Up Example Inputs
Create a template structure 'Atom' to provide the compiler with the necessary information about input parameter types. An atom is a structure with four fields (x,y,vx,vy) specifying position and velocity in Cartesian coordinates.
atom = struct('x', 0, 'y', 0, 'vx', 0, 'vy', 0);
Generate a MEX Function for Testing
Use the command codegen
with the following arguments:
-args {coder.typeof(atom, [1 Inf]),0,0}
indicates that the first argument is a row vector of atoms where the number of columns is potentially infinite. The second and third arguments are scalar double values.
-config cfg
enables dynamic memory allocation, defined by workspace variable cfg
codegen run_atoms -args {coder.typeof(atom, [1 Inf]),0,0} -config cfg -o run_atoms_mex
Code generation successful.
Run the MEX Function
The MEX function simulates 10000 atoms in approximately 1000 iteration steps given an empty list of atoms. The return value is the state of all the atoms after simulation is complete.
atoms = repmat(atom,1,0); atoms = run_atoms_mex(atoms,10000,1000)
Iteration: 50 Iteration: 100 Iteration: 150 Iteration: 200 Iteration: 250 Iteration: 300 Iteration: 350 Iteration: 400 Iteration: 450 Iteration: 500 Iteration: 550 Iteration: 600 Iteration: 650 Iteration: 700 Iteration: 750 Iteration: 800 Iteration: 850 Iteration: 900 Iteration: 950 Iteration: 1000 Completed iterations: 1000
atoms=1×10000 struct array with fields:
x
y
vx
vy
Run the MEX Function Again
Continue the simulation with another 500 iteration steps
atoms = run_atoms_mex(atoms,10000,500)
Iteration: 50 Iteration: 100 Iteration: 150 Iteration: 200 Iteration: 250 Iteration: 300 Iteration: 350 Iteration: 400 Iteration: 450 Iteration: 500 Completed iterations: 500
atoms=1×10000 struct array with fields:
x
y
vx
vy
Generate a Standalone C Code Library
To generate a C library, create a standard configuration object for libraries:
cfg = coder.config('lib');
Enable dynamic memory allocation for all variable-size arrays.
cfg.DynamicMemoryAllocationThreshold = 0;
In MATLAB the default data type is double. However, integers are usually used in C code, so pass int32
integer example values to represent the number of atoms and iterations.
codegen run_atoms -args {coder.typeof(atom, [1 Inf]),int32(0),int32(0)} -config cfg
Code generation successful.
Inspect Generated Code
When creating a library the code is generated in the folder codegen/lib/run_atoms/
. The code in this folder is self contained. To interface with the compiled C code you need only the generated header files and the library file.
dir codegen/lib/run_atoms
. .. _clang-format buildInfo.mat codeInfo.mat codedescriptor.dmr compileInfo.mat examples interface mod.c mod.h mod.o rtGetInf.c rtGetInf.h rtGetInf.o rtGetNaN.c rtGetNaN.h rtGetNaN.o rt_nonfinite.c rt_nonfinite.h rt_nonfinite.o rtw_proj.tmw rtwtypes.h run_atoms.a run_atoms.c run_atoms.h run_atoms.o run_atoms_data.h run_atoms_emxAPI.c run_atoms_emxAPI.h run_atoms_emxAPI.o run_atoms_emxutil.c run_atoms_emxutil.h run_atoms_emxutil.o run_atoms_initialize.c run_atoms_initialize.h run_atoms_initialize.o run_atoms_rtw.mk run_atoms_terminate.c run_atoms_terminate.h run_atoms_terminate.o run_atoms_types.h
Write a C Main Function
Typically, the main function is platform-dependent code that performs rendering or some other processing. In this example, a pure ANSI-C function produces a file run_atoms_state.m
which (when run) contains the final state of the atom simulation.
type run_atoms_main.c
/* Include standard C libraries */ #include <stdio.h> /* The interface to the main function we compiled. */ #include "codegen/exe/run_atoms/run_atoms.h" /* The interface to EMX data structures. */ #include "codegen/exe/run_atoms/run_atoms_emxAPI.h" int main(int argc, char **argv) { FILE *fid; int i; emxArray_Atom *atoms; /* Main arguments unused */ (void) argc; (void) argv; /* Initially create an empty row vector of atoms (1 row, 0 columns) */ atoms = emxCreate_Atom(1, 0); /* Call the function to simulate 10000 atoms in 1000 iteration steps */ run_atoms(atoms, 10000, 1000); /* Call the function again to do another 500 iteration steps */ run_atoms(atoms, 10000, 500); /* Print the result to a file */ fid = fopen("atoms_state.txt", "w"); for (i = 0; i < atoms->size[1]; i++) { fprintf(fid, "%f %f %f %f\n", atoms->data[i].x, atoms->data[i].y, atoms->data[i].vx, atoms->data[i].vy); } /* Close the file */ fclose(fid); /* Free memory */ emxDestroyArray_Atom(atoms); return(0); }
Create a Configuration Object for Executables
cfg = coder.config('exe');
cfg.DynamicMemoryAllocationThreshold = 0;
Generate a Standalone Executable
You must pass the function (run_atoms.m
) as well as custom C code (run_atoms_main.c
) The codegen
command automatically generates C code from the MATLAB code, then calls the C compiler to bundle this generated code with the custom C code (run_atoms_main.c
).
codegen run_atoms run_atoms_main.c -args {coder.typeof(atom, [1 Inf]),int32(0),int32(0)} -config cfg
Code generation successful.
Run the Executable
After simulation is complete, this produces the file atoms_state.txt
. The TXT file is a 10000x4 matrix, where each row is the position and velocity of an atom (x, y, vx, vy) representing the current state of the whole system.
system(['.' filesep 'run_atoms']);
Fetch the State
Running the executable produced atoms_state.txt
. Now, recreate the structure array from the saved matrix:
load atoms_state.txt -ascii clear atoms for i = 1:size(atoms_state,1) atoms(1,i).x = atoms_state(i,1); atoms(1,i).y = atoms_state(i,2); atoms(1,i).vx = atoms_state(i,3); atoms(1,i).vy = atoms_state(i,4); end
Render the State
Call run_atoms_mex
with zero iterations to render only.
run_atoms_mex(atoms, 10000, 0);