Specify Array Layout in Functions and Classes
By default, code generation uses column-major array layout. You can change this default
and specialize individual MATLAB® functions for row-major layout or column-major layout by inserting coder.rowMajor
or coder.columnMajor calls into the function body. Using these function
specializations, you can combine row-major data and column-major data in the generated code.
You can also specialize classes for one specific array layout. Function and class
specializations allow you to:
Incrementally modify your code for row-major layout or column-major layout.
Define array layout boundaries for applications that require different layouts in different components.
Structure the inheritance of array layout between many different functions and classes.
Input and output arguments to entry-point functions must use the same array layout. In the generated C/C++ code, the entry-point function interface accepts and returns data with the same array layout as the function array layout specification.
Specify Array Layout in a Function
For an example of a specialized function, consider addMatrixRM:
function [S] = addMatrixRM(A,B) %#codegen S = zeros(size(A)); coder.rowMajor; % specify row-major code for row = 1:size(A,1) for col = 1:size(A,2) S(row,col) = A(row,col) + B(row,col); end end
You can generate code for addMatrixRM by using the codegen command.
codegen -config:lib -launchreport addMatrixRM -args {ones(20,10),ones(20,10)}
Because of the coder.rowMajor call, the code generator produces code that uses data stored in row-major layout.
Other functions called from a row-major function or column-major function inherit the same array layout. If a called function has its own distinct coder.rowMajor or coder.columnMajor call, the local call takes precedence.
You can mix column-major and row-major functions in the same code. The code generator inserts transpose or conversion operations when passing data between row-major and column-major functions. These conversion operations ensure that array elements are stored as required by functions with different array layout specifications. For example, the inputs to a column-major function, called from a row-major function, are converted to column-major layout before being passed to the column-major function.
Query Array Layout of a Function
To query the array layout of a function at code generation time, use coder.isColumnMajor and coder.isRowMajor. This query can be useful for specializing your
generated code when it involves row-major and column-major functions. For example,
consider this function:
function [S] = addMatrixRouted(A,B) %#codegen if coder.isRowMajor %execute this code if row-major S = addMatrixRM(A,B); elseif coder.isColumnMajor %execute this code if column-major S = addMatrix_OptimizedForColumnMajor(A,B); end
This function behaves differently depending on whether the code generator uses a row-major or
column-major layout. When addMatrixRouted is row-major, it calls the
addMatrixRM function, which has efficient memory access for
row-major data. When the function is column-major, it calls a version of the
addMatrixRM function optimized for column-major data.
When the code generator uses a column-major layout, addMatrixRouted calls
addMatrix_OptimizedForColumnMajor, which iterates through the
columns in the outer loop and the rows in the inner
loop.
function [S] = addMatrix_OptimizedForColumnMajor(A,B) %#codegen S = zeros(size(A)); for col = 1:size(A,2) for row = 1:size(A,1) S(row,col) = A(row,col) + B(row,col); end end
The code generator optimizes the generated code for column-major data and produces code has a stride length of only one element.
...
/* column-major layout */
for (col = 0; col < 10; col++) {
for (row = 0; row < 20; row++) {
S[row + 20 * col] = A[row + 20 * col] + B[row + 20 * col];
}
}
...Because it defines separate execution paths for row-major and column-major data, the generated
code for addMatrixRouted provides efficient memory access for either
array layout.
Specify Array Layout in a Class
You can specify array layout for a class so that object property variables are stored with a specific array layout. To specify the array layout, place a coder.rowMajor or coder.columnMajor call in the class constructor. If you assign an object with a specified array layout to the property of another object, the array layout of the assigned object takes precedence.
Consider the row-major class rowMats as an example. This class contains matrix properties and a method that consists of an element-wise addition algorithm. The algorithm in the method performs more efficiently for data stored in row-major layout. By specifying coder.rowMajor in the class constructor, the generated code uses row-major layout for the property data.
classdef rowMats properties (Access = public) A B C end methods function obj = rowMats(A,B) coder.rowMajor; if nargin == 0 obj.A = 0; obj.B = 0; obj.C = 0; else obj.A = A; obj.B = B; obj.C = zeros(size(A)); end end function obj = add(obj) for row = 1:size(obj.A,1) for col = 1:size(obj.A,2) obj.C(row,col) = obj.A(row,col) + obj.B(row,col); end end end end end
Use the class in the function doMath. The inputs and outputs of the
entry-point function must all use the same array
layout.
function out = doMath(in1,in2) %#codegen out = zeros(size(in1)); myMats = rowMats(in1,in2); myMats = myMats.add; out = myMats.C; end
Generate a C static library for the doMath function by using the
codegen command with the -config:lib option.
Use the -args option with coder.typeof to specify that the input arguments are fixed-size 20-by-10
arrays of doubles.
codegen -config:lib doMath -args {coder.typeof(0,[20,10]),coder.typeof(0,[20,10])}
Examine the definition of the doMath C function in the file
doMath.c.
void doMath(const double in1[200], const double in2[200], double out[200])
{
double myMats_C[200];
int col;
int row;
for (row = 0; row < 20; row++) {
for (col = 0; col < 10; col++) {
int myMats_C_tmp;
myMats_C_tmp = row + 20 * col;
myMats_C[col + 10 * row] = in1[myMats_C_tmp] + in2[myMats_C_tmp];
}
}
for (row = 0; row < 10; row++) {
for (col = 0; col < 20; col++) {
out[col + 20 * row] = myMats_C[row + 10 * col];
}
}
}in1 and in2
to a row-major layout before calling the class constructor. Similarly, it converts the
doMath function output back to column-major layout.For comparison, generate code that assumes a row-major array layout by using the
codegen command with the -rowmajor
option.
codegen -config:lib -rowmajor doMath -args {coder.typeof(0,[20,10]),coder.typeof(0,[20,10])}
Examine the generated code.
void doMath(const double in1[200], const double in2[200], double out[200])
{
int col;
int row;
for (row = 0; row < 20; row++) {
for (col = 0; col <= 8; col += 2) {
int i;
i = col + 10 * row;
_mm_storeu_pd(&out[i],
_mm_add_pd(_mm_loadu_pd(&in1[i]), _mm_loadu_pd(&in2[i])));
}
}
}The code generator does not generate code to convert arrays from column-major to row-major layouts.
When designing a class for a specific array layout, consider:
If you do not specify the array layout in a class constructor, objects inherit their array layout from the function that calls the class constructor, or from code generation configuration settings.
You cannot specify the array layout in a nonstatic method by using
coder.rowMajororcoder.columnMajor. Methods use the same array layout as the receiving object. Methods do not inherit the array layout of the function that calls them. For static methods, which are used similarly to ordinary functions, you can specify the array layout in the method.If you specify the array layout of a superclass, the subclass inherits this array layout specification. You cannot specify conflicting array layouts between superclasses and subclasses.
See Also
coder.columnMajor | coder.rowMajor | coder.isRowMajor | coder.isColumnMajor | codegen