Generate Code That Uses Row-Major Array Layout
Array layout can be important for integration, usability, and performance. The code generator produces code that uses column-major layout by default. However, many devices, sensors, and libraries use row-major array layout for their data. You can apply your code directly to this data by generating code that uses row-major layout. Array layout can also affect performance. Many algorithms perform memory access more efficiently for one specific array layout.
You can specify row-major array layout at the command line, with code generation configuration properties, or by using the MATLAB® Coder™ app. You can also specify row-major layout or column-major layout for individual functions and classes. The inputs and outputs of your entry-point (top-level) functions must all use the same array layout.
Specify Row-Major Layout
Consider this function for adding two matrices. The algorithm performs the addition through explicit row and column traversal.
function [S] = addMatrix(A,B) %#codegen 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
Generate C code for addMatrix
by using the
-rowmajor
option. Specify the form of the input parameters by
using the -args
option and launch the code generation
report.
codegen addMatrix -args {ones(20,10),ones(20,10)} -config:lib -launchreport -rowmajor
Alternatively, configure your code for row-major layout by modifying the
RowMajor
parameter in the code generation configuration
object. You can use this parameter with any type
of configuration object: lib
, mex
,
dll
, or exe
.
cfg = coder.config('lib'); cfg.RowMajor = true; codegen addMatrix -args {ones(20,10),ones(20,10)} -config cfg -launchreport
Code generation results in this C code:
... /* 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]; } } ...
To specify row-major layout with the MATLAB Coder app:
Open the Generate dialog box. On the Generate Code page, click the Generate arrow .
Click More Settings.
On the Memory tab, set Array layout:
Row-major
.
To verify that your generated code uses row-major layout, compare the array indexing in your generated code with the array indexing in code that uses column-major layout. You can also generate code that uses N-dimensional indexing. N-dimensional indexing can make differences in array layout more apparent. For more information, see Generate Code That Uses N-Dimensional Indexing.
MATLAB stores data in column-major layout by default. When you call a
generated MEX function that uses row-major layout, the software automatically
converts input data from column-major layout to row-major layout. Output data
returned from the MEX function is converted back to column-major layout. For
standalone lib
, dll
, and
exe
code generation, the code generator assumes that
entry-point function inputs and outputs are stored with the same array layout as the
function.
Array Layout and Algorithmic Efficiency
For certain algorithms, row-major layout provides more efficient memory access.
Consider the C code shown for addMatrix
that uses row-major
layout. 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, generate code that uses column-major layout:
codegen addMatrix -args {ones(20,10),ones(20,10)} -config:lib -launchreport
Code generation produces this C code:
... /* 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) %#codegen 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
Generate code that uses row-major layout:
codegen addMatrix3D -args {ones(20,10,5),ones(20,10,5)} -config:lib -launchreport -rowmajor
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. Compare the differences to
generated 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 % 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:
To generate code, enter:
codegen -config:lib myMixedFn1 -args {ones(20,10),ones(20,10)} -rowmajor -launchreport
See Also
coder.columnMajor
| coder.rowMajor
| coder.ceval
| coder.isRowMajor
| coder.isColumnMajor
| codegen