Interface with Row-Major Data in MATLAB Function Blocks
Array layout can be important for integration, usability, and
performance. Simulink® uses column-major layout by default, as does the MATLAB
Function block. However, many devices, sensors, and libraries use row-major
array layout for their data. You can apply your model directly to this data by using the
coder.ceval
function with row-major
layout in a MATLAB Function block.
Array layout can also affect performance. Many algorithms perform memory access more efficiently for one specific array layout.
Row-Major Layout in Simulation and Code Generation
For the MATLAB Function block, you can specify row-major array layout inside the block. This specification occurs at the function level and does not alter the array layout of the model outside of the function. The array layout that you specify inside the MATLAB Function block applies to both simulation and C/C++ code generation. See Specify Array Layout in Functions and Classes.
For C/C++ code generation using Simulink Coder™ and Embedded Coder® software, you can specify array layout at the model level, which is supported by MATLAB Function blocks. For more information on controlling array layout at the model level, see Code Generation of Matrices and Arrays (Simulink Coder). The model code generation setting for array layout has no affect for simulation. See Array layout (Simulink Coder). For examples of using row-major layout in MATLAB Function block for code generation, see Generate Row-Major Code for Model That Contains a MATLAB Function Block (Simulink Coder).
For the MATLAB Function block, the array layout specification at the function level takes precedence over the array layout specification of the model. However, for global and persistent variables, the array layout specification of the model takes precedence.
Array Layout Conversions
MATLAB® and Simulink store data in column-major layout by default. The software automatically inserts array layout conversions as needed when you specify different array layouts in different functions and at different boundaries.
For example, when you simulate a model or generate code for a model that uses column-major layout, and the model contains a MATLAB Function block that uses row-major layout, then the software converts the block input data to row-major and the block output data back to column-major, as needed. Array layout conversions can affect performance. For more information on performance considerations for array layout, see Code Design for Row-Major Array Layout.
Array Layout and Algorithmic Efficiency
For certain algorithms, row-major layout provides more efficient memory access. Consider this function for adding two matrices. The algorithm performs the addition through explicit row and column traversal.
function [S] = addMatrix(A,B) coder.rowMajor; S = zeros(size(A)); for row = 1:size(A,1) for col = 1:size(A,2) S(row,col) = A(row,col) + B(row,col); end end
If you use this code in a MATLAB Function block, code generation results in this C code for the function:
...
/* generated code for addMatrix using row-major */
for (row = 0; row < 20; row++) {
for (col = 0; col < 10; col++) {
S[col + 10 * row] = A[col + 10 * row] + B[col + 10 * row];
}
}
...
The arrays are indexed by the generated code using the formula:
[col + 10 * row]
Because the arrays are stored in row-major layout, adjacent memory elements are separated by single column increments. The stride length for the algorithm is equal to one. The stride length is the distance in memory elements between consecutive memory accesses. A shorter stride length provides more efficient memory access.
Using column-major layout for the data results in a longer stride length and less
efficient memory access. To see this comparison, consider the generated C code for
addMatrix
that uses column-major layout:
...
/* generated code for addMatrix using column-major */
for (row = 0; row < 20; row++) {
for (col = 0; col < 10; col++) {
S[row + 20 * col] = A[row + 20 * col] + B[row + 20 * col];
}
}
...
In column-major layout, the column elements are contiguous in memory in the generated code. Adjacent memory elements are separated by single row increments and indexed by the formula:
[row + 20 * col]
However, the algorithm iterates through the columns in the inner for-loop. Therefore, the column-major C code must make a stride of 20 elements for each consecutive memory access.
The array layout that provides the most efficient memory access depends on the algorithm. For this algorithm, row-major layout of the data provides more efficient memory access. The algorithm traverses over the data row by row. Row-major storage is therefore more efficient.
Row-Major Layout for N-Dimensional Arrays
You can use row-major layout for N-dimensional arrays. When an array is stored in row-major layout, the elements from the last (rightmost) dimension or index are contiguous in memory. In column-major layout, the elements from the first (leftmost) dimension or index are contiguous.
Consider the example function addMatrix3D
, which accepts
three-dimensional inputs.
function [S] = addMatrix3D(A,B) coder.rowMajor; S = zeros(size(A)); for i = 1:size(A,1) for j = 1:size(A,2) for k = 1:size(A,3) S(i,j,k) = A(i,j,k) + B(i,j,k); end end end end
The code generator produces this C code:
...
/* row-major layout */
for (i = 0; i < 20; i++) {
for (j = 0; j < 10; j++) {
for (k = 0; k < 5; k++) {
S[(k + 5 * j) + 50 * i] = A[(k + 5 * j) + 50 * i]
+ B[(k + 5 * j) + 50 * i];
}
}
}
...
In row-major layout, adjacent memory elements are separated by single increments of
the last index, k
. The inner for-loop iterates over adjacent elements
separated by only one position in memory.
Remove the coder.rowMajor
call and generate C code that uses
column-major layout:
...
/* column-major layout */
for (i = 0; i < 20; i++) {
for (j = 0; j < 10; j++) {
for (k = 0; k < 5; k++) {
S[(i + 20 * j) + 200 * k] = A[(i + 20 * j) + 200 * k]
+ B[(i + 20 * j) + 200 * k];
}
}
}
...
In column-major layout, adjacent elements are separated by single increments of the
first index, i
. The inner for-loop now iterates over adjacent
elements separated by 200 positions in memory. The long stride length can cause
performance degradation due to cache misses.
Because the algorithm iterates through the last index, k
, in the
inner for-loop, the stride length is much longer for the generated code that uses
column-major layout. For this algorithm, row-major layout of the data provides more
efficient memory access.
Specify Array Layout in External Function Calls
To call external C/C++ functions that expect data stored with a specific layout, use
coder.ceval
with the layout
syntax. If you
do not use this syntax, the external function inputs and outputs are assumed to use
column-major layout by default.
Consider an external C function designed to use row-major layout called
myCFunctionRM
. To integrate this function into your code, call
the function using the '-layout:rowMajor'
or
'-row'
option. This option ensures that the input and output
arrays are stored in row-major order. The code generator automatically inserts array
layout conversions as needed.
coder.ceval('-layout:rowMajor','myCFunctionRM',coder.ref(in),coder.ref(out))
Within a MATLAB function that uses row-major layout, you may seek to call an external
function designed to use column-major layout. In this case, use the
'-layout:columnMajor'
or '-col'
option.
coder.ceval('-layout:columnMajor','myCFunctionCM',coder.ref(in),coder.ref(out))
You can perform row-major and column-major function calls in the same code. Consider
the function myMixedFn1
as an example:
function [E] = myMixedFn1(x,y) %#codegen coder.rowMajor; % specify type of return arguments for ceval calls D = zeros(size(x)); E = zeros(size(x)); % include external C functions that use row-major & column-major coder.cinclude('addMatrixRM.h'); coder.updateBuildInfo('addSourceFiles', 'addMatrixRM.c'); coder.cinclude('addMatrixCM.h'); coder.updateBuildInfo('addSourceFiles', 'addMatrixCM.c'); % call C function that uses row-major order coder.ceval('-layout:rowMajor','addMatrixRM', ... coder.rref(x),coder.rref(y),coder.wref(D)); % call C function that uses column-major order coder.ceval('-layout:columnMajor','addMatrixCM', ... coder.rref(x),coder.rref(D),coder.wref(E)); end
The external files are:
See Also
coder.rowMajor
| coder.columnMajor
| coder.isColumnMajor
| coder.ceval
| coder.isRowMajor