Hauptinhalt

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];
    }
  }
}
Because the code generator assumes a column-major layout by default, the generated code converts 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.rowMajor or coder.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

| | | |

Topics