Main Content

関数とクラスにおける配列レイアウトの指定

関数本体に coder.rowMajor または coder.columnMajor 呼び出しを挿入すると、個々の MATLAB® 関数を行優先のレイアウトまたは列優先のレイアウトに特殊化することができます。このような関数の特殊化を使用することで、生成されたコード内で行優先のデータと列優先のデータを併用することができます。また、クラスを特定の配列レイアウトに特殊化することもできます。関数およびクラスの特殊化を利用すると、以下が可能になります。

  • コードを、行優先のレイアウトまたは列優先のレイアウトにインクリメンタルに変更する。

  • 異なるコンポーネントに異なるレイアウトを要するアプリケーションにおいて、配列レイアウトの境界を定義する。

  • 多くの異なる関数およびクラス間で、配列レイアウトの継承を構造化する。

MATLAB Coder™ エントリポイント (最上位) 関数では、すべての入力と出力が同じ配列レイアウトを使用しなければなりません。生成された C/C++ コードにおいて、エントリポイント関数インターフェイスは、関数配列のレイアウトの指定と同じ配列のレイアウトをもつデータを受け入れて返します。

メモ

コード生成では、既定で列優先の配列レイアウトが使用されます。

関数における配列レイアウトの指定

特殊化された関数の例として、次の 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

MATLAB Coder では、codegen コマンドを使用して、addMatrixRM のコードを生成できます。

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

coder.rowMajor 呼び出しがあるため、コード ジェネレーターは行優先のレイアウトで格納されたデータを使用するコードを生成します。

行優先の関数または列優先の関数から呼び出されたその他の関数は、同じ配列レイアウトを継承します。呼び出された関数がそれぞれ独自の coder.rowMajor または coder.columnMajor 呼び出しを含む場合、ローカルな呼び出しが優先されます。

同じコードの中で、列優先の関数と行優先の関数を混在させることができます。列優先の関数と行優先の関数間でデータを受け渡すとき、コード ジェネレーターが転置または変換演算を挿入します。これらの変換演算により、配列要素が、異なる配列レイアウト仕様をもつ関数によって要求される形式で格納されるようになります。たとえば、行優先の関数から列優先の関数を呼び出す場合、その入力は、列優先の関数に受け渡される前に列優先のレイアウトに変換されます。

関数の配列レイアウトのクエリ

コンパイル時に関数の配列レイアウトをクエリするには、coder.isRowMajor または coder.isColumnMajor を使用します。このクエリは、行優先の関数と列優先の関数が関与するとき、生成されたコードを特殊化するのに役立つ場合があります。たとえば、以下の関数を考えます。

function [S] = addMatrixRouted(A,B)
 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

この関数は、行優先か、列優先かによって異なる動作をします。addMatrixRouted が行優先のとき、行優先のデータでメモリ アクセス効率が向上する関数 addMatrixRM を呼び出します。関数が列優先のとき、列優先のデータに最適化されたバージョンの関数 addMatrixRM を呼び出します。

たとえば、以下の関数定義を考えます。関数 addMatrixRM とは対照的に、このアルゴリズムは、外側のループでは列を反復し、内側のループでは行を反復します。

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

この関数のコード生成では以下が得られます。

... 
/* 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];  
  }
}
...

生成されたコードのストライド長は、わずか 1 要素です。特殊化のクエリにより、addMatrixRouted の生成されたコードは、どちらの配列レイアウトが選択された場合でも効率的なメモリ アクセスを提供します。

クラスにおける配列レイアウトの指定

クラスに配列レイアウトを指定して、オブジェクト プロパティ変数が特定の配列レイアウトで格納されるようにできます。配列レイアウトを指定するには、クラス コンストラクターに coder.rowMajor または coder.columnMajor 呼び出しを配置します。配列レイアウトが指定されたオブジェクトを別のオブジェクトのプロパティに割り当てた場合、割り当てられたオブジェクトの配列レイアウトが優先されます。

例として、行優先のクラス rowMats を考えます。このクラスには、行列プロパティと、要素単位の加算アルゴリズムで構成されるメソッドが含まれています。メソッドに含まれるアルゴリズムは、行優先のレイアウトで格納されたデータにおいて効率的に実行されます。クラス コンストラクターに coder.rowMajor を指定すると、生成されたコードは、プロパティ データに行優先のレイアウトを使用するようになります。

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

このクラスを、シンプルな関数 doMath で使用します。エントリポイント関数の入力と出力はすべて、同じ配列レイアウトでなければなりません。

function [out] = doMath(in1,in2)
%#codegen
out = zeros(size(in1));
myMats = rowMats(in1,in2);
myMats = myMats.add;
out = myMats.C;
end

MATLAB Coder では、次を入力してコードを生成できます。

A = rand(20,10);
B = rand(20,10);
cfg = coder.config('lib');
codegen -config cfg doMath -args {A,B} -launchreport

既定の設定では、関数 doMath に行優先のレイアウトを指定していないため、コード ジェネレーターは、エントリポイント関数の入力と出力が列優先のレイアウトであるものと想定します。そのため、クラス コンストラクターを呼び出す前に、生成されたコードでは in1in2 を行優先のレイアウトに変換します。同様に、関数 doMath の出力は、列優先のレイアウトに再変換されます。

特定の配列レイアウトについてクラスを設計する場合、以下を考慮します。

  • クラス コンストラクターに配列レイアウトを指定しない場合、オブジェクトの配列レイアウトは、クラス コンストラクターの呼び出し元の関数から、またはコード生成構成設定から継承されます。

  • 静的でないメソッドでは、coder.rowMajor または coder.columnMajor を使用して配列レイアウトを指定することはできません。メソッドは、受け取りオブジェクトと同じ配列レイアウトを使用します。メソッドは、呼び出し元関数の配列レイアウトを継承しません。通常の関数と同じように利用される静的なメソッドでは、メソッドに配列レイアウトを指定できます。

  • スーパークラスに配列レイアウトを指定すると、サブクラスはその配列レイアウト指定を継承します。スーパークラスとサブクラスとで競合する配列レイアウトを指定することはできません。

参考

| | | |

関連するトピック