Main Content

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:

  1. Open the Generate dialog box. On the Generate Code page, click the Generate arrow .

  2. Click More Settings.

  3. 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:

 addMatrixRM.h

 addMatrixRM.c

 addMatrixCM.h

 addMatrixCM.c

To generate code, enter:

codegen -config:lib myMixedFn1 -args {ones(20,10),ones(20,10)} -rowmajor -launchreport

See Also

| | | | |

Related Topics