Variable-size data is data whose size might change at run time. You can use MATLAB® Coder™ to generate C/C++ code from MATLAB code that uses variable-size data. MATLAB supports bounded and unbounded variable-size data for code generation. Bounded variable-size data has fixed upper bounds. This data can be allocated statically on the stack or dynamically on the heap. Unbounded variable-size data does not have fixed upper bounds. This data must be allocated on the heap. By default, for MEX and C/C++ code generation, support for variable-size data is enabled and dynamic memory allocation is enabled for variable-size arrays whose size is greater than or equal to a configurable threshold.
By default, for MEX and C/C++ code generation, support for variable-size data is enabled. You modify variable sizing settings from the project settings dialog box, the command line, or using dialog boxes.
To open the Generate dialog
box, on the Generate Code page, click the Generate arrow
.
Click More Settings.
On the Memory tab, select or clear Enable variable-sizing.
Create a configuration object for code generation. For example, for a library:
cfg = coder.config('lib');
Set the EnableVariableSizing
option:
cfg.EnableVariableSizing = false;
Using the -config
option, pass
the configuration object to codegen
:
codegen -config cfg foo
By default, dynamic memory allocation is enabled for variable-size arrays whose size is greater than or equal to a configurable threshold. If you disable support for variable-size data (see Disable Support for Variable-Size Data), you also disable dynamic memory allocation. You can modify dynamic memory allocation settings from the project settings dialog box or the command line.
To open the Generate dialog
box, on the Generate Code page, click the Generate arrow
.
Click More Settings.
On the Memory tab, set Dynamic memory allocation to one of the following options:
Setting | Action |
---|---|
Never | Dynamic memory allocation is disabled. Variable-size data is allocated statically on the stack. |
For all variable-sized
arrays | Dynamic memory allocation is enabled for variable-size arrays. Variable-size data is allocated dynamically on the heap. |
For arrays with max size at or above threshold | Dynamic memory allocation is enabled for variable-size arrays whose size is greater than or equal to the Dynamic memory allocation threshold. Variable-size arrays whose size is less than this threshold are allocated on the stack. |
Optionally, if you set Dynamic memory allocation to For
arrays with maximum size at or above threshold
, configure Dynamic
memory allocation threshold to fine-tune memory allocation.
Create a configuration object for code generation. For example, for a MEX function:
mexcfg = coder.config('mex');
Set the DynamicMemoryAllocation
option:
Setting | Action |
---|---|
mexcfg.DynamicMemoryAllocation='Off'; | Dynamic memory allocation is disabled. Variable-size data is allocated statically on the stack. |
mexcfg.DynamicMemoryAllocation='AllVariableSizeArrays'; | Dynamic memory allocation is enabled for variable-size arrays. Variable-size data is allocated dynamically on the heap. |
mexcfg.DynamicMemoryAllocation='Threshold'; | Dynamic memory allocation is enabled for variable-size arrays
whose size (in bytes) is greater than or equal to the value specified
using the Dynamic memory allocation threshold parameter.
Variable-size arrays whose size is less than this threshold are allocated
on the stack. |
Optionally, if you set Dynamic memory allocation
to ‘Threshold'
,
configure Dynamic memory allocation threshold
to
fine tune memory allocation.
Using the -config
option, pass
the configuration object to codegen
:
codegen -config mexcfg foo
Here is a basic workflow that first generates MEX code for verifying the generated code and then generates standalone code after you are satisfied with the result of the prototype.
To work through these steps with a simple example, see Generate Code for a MATLAB Function That Expands a Vector in a Loop
In the MATLAB Editor, add the compilation directive %#codegen
at
the top of your function.
This directive:
Indicates that you intend to generate code for the MATLAB algorithm
Turns on checking in the MATLAB Code Analyzer to detect potential errors during code generation
Address issues detected by the Code Analyzer.
In some cases, the MATLAB Code Analyzer warns you when your code assigns data a fixed size but later grows the data, such as by assignment or concatenation in a loop. If that data is supposed to vary in size at run time, you can ignore these warnings.
Generate a MEX function using codegen
to
verify the generated code. Use the following command-line options:
-args {coder.typeof...}
if you
have variable-size inputs
-report
to generate a code generation
report
For example:
codegen -report foo -args {coder.typeof(0,[2 4],1)}
coder.typeof
to
specify one variable-size input for function foo
.
The first argument, 0
, indicates the input data
type (double
) and complexity (real
).
The second argument, [2 4]
, indicates the size,
a matrix with two dimensions. The third argument, 1
,
indicates that the input is variable sized. The upper bound is 2 for
the first dimension and 4 for the second dimension.Note:
During compilation, |
Fix size mismatch errors:
Cause | How To Fix | For More Information |
---|---|---|
You try to change the size of data after its size has been locked. | Declare the data to be variable sized. | See Diagnosing and Fixing Size Mismatch Errors. |
Fix upper bounds errors
Cause | How To Fix | For More Information |
---|---|---|
MATLAB cannot determine or compute the upper bound | Specify an upper bound. | See Specifying Upper Bounds for Variable-Size Data and Diagnosing and Fixing Size Mismatch Errors. |
MATLAB attempts to compute an upper bound for unbounded variable-size data. | If the data is unbounded, enable dynamic memory allocation. | See Control Dynamic Memory Allocation. |
Generate C/C++ code using the codegen
function.
This example uses the function myuniquetol
.
This function returns in vector B
a version of
input vector A
, where the elements are unique to
within tolerance tol
of each other. In vector B
, abs
(B
(i
)
- B
(j
)) > tol
for
all i
and j
.
Initially, assume input vector A
can store up to
100 elements.
function B = myuniquetol(A, tol) A = sort(A); B = A(1); k = 1; for i = 2:length(A) if abs(A(k) - A(i)) > tol B = [B A(i)]; k = i; end end
Add the %#codegen
compilation directive at
the top of the function:
function B = myuniquetol(A, tol) %#codegen A = sort(A); B = A(1); k = 1; for i = 2:length(A) if abs(A(k) - A(i)) > tol B = [B A(i)]; k = i; end end
The Code Analyzer detects that variable B
might
change size in the for-
loop. It issues this warning:
The variable 'B' appears to change size on every loop iteration. Consider preallocating for speed.
In this function, you expect vector B
to
expand in size because it adds values from vector A
.
Therefore, you can ignore this warning.
It is a best practice to generate MEX code before you generate C/C++ code. Generating MEX code can identify code generation issues that are harder to detect at run time.
Generate a MEX function for myuniquetol
:
codegen -report myuniquetol -args {coder.typeof(0,[1 100],1),coder.typeof(0)}
What do these command-line options mean?
Code generation is successful. codegen
does
not detect issues. In the current folder, codegen
generates
a MEX function for myuniquetol
and provides a
link to the code generation report.
Click the View report link.
In the code generation report, select the Variables tab.
The size of A
is 1x:100
because
you specified that A
is variable size with an upper
bound of 100
. The size of variable B
is 1x:?
,
indicating that it is variable size with no
upper bounds.
Generate C code for variable-size inputs. By default, codegen
allocates
memory statically for data whose size is less than the dynamic memory
allocation threshold of 64 kilobytes. If the size of the data is greater
than or equal to the threshold or is unbounded, codegen
allocates
memory dynamically on the heap.
Create a configuration option for C library generation:
cfg=coder.config('lib');
Issue this command:
codegen -config cfg -report myuniquetol -args {coder.typeof(0,[1 100],1),coder.typeof(0)}
codegen
generates a static library in the
default location, codegen\lib\myuniquetol
and provides
a link to the code generation report.
Click the View report link.
In the code generation report, click the C code tab.
On the C code tab, click the
link to myuniquetol.h
.
The function declaration is:
extern void myuniquetol(const double A_data[], const int A_size[2], double tol, emxArray_real_T *B);
codegen
computes the size of A
and,
because its maximum size is less than the default dynamic memory allocation
threshold of 64k bytes, allocates this memory statically. The generated
code contains:
double A_data[]
: the definition
of A
.
int A_size[2]
: the actual size
of the input.
The code generator determines that B
is variable
size with unknown upper bounds. It represents B
as emxArray_real_T
. MATLAB provides utility
functions for creating and interacting with emxArrays
in
your generated code. For more information, see C Code Interface for Arrays.
You specified that the input A
is variable
size with an upper bound of 100
. Therefore, you
know that the output B
cannot be larger than 100
elements.
Use coder.varsize
to indicate
that B
is variable size with an upper bound of 100
.
function B = myuniquetol(A, tol) %#codegen A = sort(A); coder.varsize('B', [1 100], [0 1]); B = A(1); k = 1; for i = 2:length(A) if abs(A(k) - A(i)) > tol B = [B A(i)]; k = i; end end
Generate code.
codegen -config cfg -report myuniquetol -args {coder.typeof(0,[1 100],1),coder.typeof(0)}
The function declaration is:
extern void myuniquetol(const double A_data[], const int A_size[2], double tol, double B_data[], int B_size[2]);
The code generator statically allocates the memory for B
.
It stores the size of B
in int B_size[2]
.
In this step, you reduce the dynamic memory allocation threshold
and generate code for an input that exceeds this threshold. This step
specifies that the second dimension of A
has an
upper bound of 10000
.
Change the upper bound of B
to
match the upper bound of A
.
function B = myuniquetol(A, tol) %#codegen A = sort(A); coder.varsize('B', [1 10000], [0 1]); B = A(1); k = 1; for i = 2:length(A) if abs(A(k) - A(i)) > tol B = [B A(i)]; k = i; end end
Set the dynamic memory allocation threshold to 4 kilobytes
and generate code where the size of input A
exceeds
this threshold.
cfg.DynamicMemoryAllocationThreshold=4096; codegen -config cfg -report myuniquetol -args {coder.typeof(0,[1 10000],1),coder.typeof(0)}
View the generated code in the report. Because the
maximum size of A
and B
now
exceed the dynamic memory allocation threshold, codegen
allocates A
and B
dynamically
on the heap. In the generated code, A
and B
have
type emxArray_real_T
.
extern void myuniquetol(const emxArray_real_T *A, double tol, emxArray_real_T *B);
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.
Create a New Folder and Copy Relevant Files
The following code will create a folder in your current working folder (pwd). The new folder will contain only the files that are relevant for this example. If you do not want to affect the current folder (or if you cannot generate files in this folder), change your working folder.
Run Command: Create a New Folder and Copy Relevant Files
coderdemo_setup('coderdemo_atoms');
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 variable size matrices. cfg.DynamicMemoryAllocation = 'AllVariableSizeArrays';
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
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
cfg.DynamicMemoryAllocation = 'AllVariableSizeArrays';
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
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
. rt_nonfinite.h run_atoms_emxutil.o .. rt_nonfinite.o run_atoms_initialize.c buildInfo.mat rtw_proj.tmw run_atoms_initialize.h codeInfo.mat rtwtypes.h run_atoms_initialize.o examples run_atoms.a run_atoms_ref.rsp interface run_atoms.c run_atoms_rtw.mk rtGetInf.c run_atoms.h run_atoms_terminate.c rtGetInf.h run_atoms.o run_atoms_terminate.h rtGetInf.o run_atoms_emxAPI.c run_atoms_terminate.o rtGetNaN.c run_atoms_emxAPI.h run_atoms_types.h rtGetNaN.h run_atoms_emxAPI.o rtGetNaN.o run_atoms_emxutil.c rt_nonfinite.c run_atoms_emxutil.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/lib/run_atoms/run_atoms.h" /* The interface to EMX data structures. */ #include "codegen/lib/run_atoms/run_atoms_emxAPI.h" int main(int argc, char **argv) { 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 standard output */ for (i = 0; i < atoms->size[1]; i++) { printf("%f %f %f %f\n", atoms->data[i].x, atoms->data[i].y, atoms->data[i].vx, atoms->data[i].vy); } /* Free memory */ emxDestroyArray_Atom(atoms); return(0); }
Create a Configuration Object for Executables
cfg = coder.config('exe'); cfg.DynamicMemoryAllocation = 'AllVariableSizeArrays';
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
Run the Executable
After simulation is complete, this produces the file 'atoms_state.mat'. The MAT 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.
[~,atoms_data] = system(['.' filesep 'run_atoms']); fh = fopen('atoms_state.mat', 'w'); fprintf(fh, '%s', atoms_data); fclose(fh);
Fetch the State
Running the executable produced 'atoms_state.mat'. Now, recreate the structure array from the saved matrix
load atoms_state.mat -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);
Clean Up
Remove files and return to original folder
Run Command: Cleanup
if ispc delete run_atoms.exe else delete run_atoms end delete atoms_state.mat cleanup