Main Content

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:

 addMatrixRM.h

 addMatrixRM.c

 addMatrixCM.h

 addMatrixCM.c

See Also

| | | |

Related Topics