Main Content

N 次元インデックスを使用するコードの生成

既定では、コード ジェネレーターは配列に 1 次元インデックスを使用します。コード ジェネレーターは、MATLAB® コードでの N 次元配列に対して C/C++ コードで 1 次元配列を作成します。N 次元インデックスを使用して可読性を向上させ、インターフェイスを生成されたコードに適用することができます。

以下の表は、N 次元インデックスが有効の場合と無効の場合に生成されるコードの違いの例を示しています。

MATLAB コード

生成される C コード (既定)

N 次元インデックス有効時に生成される C コード

A = zeros(2,4,6)
A[48]
  • 列優先の配列レイアウト (既定) の場合:

    A[6][4][2]
    
  • 行優先の配列レイアウトが有効になっている場合:

    A[2][4][6]
    

MATLAB は、既定で列優先の配列レイアウトを使用するコードを生成するため、N 次元インデックスのインデックスの次元は逆になります。インデックスの次元を切り替えるために、行優先の配列レイアウトを有効にできます。

N 次元配列の 1 次元への変換は、配列の "フラット化" とも呼ばれます。コンピューター メモリでは、すべてのデータは 1 次元の配列に格納されます。インデックスの選択により計算結果が変わることはありません。ただし、コードに配列の入力または出力がある場合、生成されたコードに対するインターフェイスが変更される可能性があります。

N 次元インデックスを有効にするには、次の手順に従います。

  • -preservearraydims オプションを使用します。

    codegen foo -preservearraydims
  • コード生成構成オブジェクトの PreserveArrayDimensions プロパティを true に設定します、次に例を示します。

    cfg = coder.config('lib');
    cfg.PreserveArrayDimensions = true;
    codegen foo -config cfg

MATLAB Coder™ アプリから N 次元インデックスを有効にするには、次の手順に従います。

  • コード生成ワークフローにおいて [コード生成] ページに移動します。

  • [生成] 矢印 をクリックして [生成] ダイアログ ボックスを開きます。

  • [詳細設定] をクリックします。

  • [メモリ] タブで、[配列の次元を保持] チェック ボックスをオンにします。

N 次元インデックスおよび行優先のレイアウトによる可読性の向上

N 次元インデックスにより、生成された C/C++ コードの MATLAB コードへのトレースが容易になります。コード ジェネレーターは、配列を 1 次元に変換せず、元の配列の次元を保持します。さらに、行優先のレイアウトを指定して、コード外観をさらに直感的にすることができます。

2 つの行列を 1 要素ずつ追加する、MATLAB 関数 addMatrices について考えます。

function sum = addMatrices(A,B) 
%#codegen
sum = coder.nullcopy(A);
for row = 1:size(A,1) 
   for col = 1:size(A,2)  
       sum(row,col) = A(row,col) + B(row,col);
   end
end

2 行 4 列の配列を操作するように、addMatrices のコードを生成します。N 次元インデックスおよび行優先の配列レイアウトを有効にします。

cfg = coder.config('lib');
cfg.PreserveArrayDimensions = true; 
cfg.RowMajor = true; 
codegen addMatrices -args {ones(2,4),ones(2,4)} -config cfg -launchreport

コード生成により、明示的な 2 次元配列インデックスを使用するコードが生成されます。

/* N-d indexing on, row-major on */
void addMatrices(double A[2][4], double B[2][4], double sum[2][4])
{
  int row;
  int col;
  for (row = 0; row < 2; row++) {
    for (col = 0; col < 4; col++) {
      sum[row][col] = A[row][col] + B[row][col];
    }
  }
}

addMatrices に対して生成されたコードは、元の MATLAB コードと同じ 2 次元インデックスを使用します。生成されたコードを元のアルゴリズムと比較して容易に解析できます。行優先のレイアウトの使用方法を理解するには、行優先の配列レイアウトを使用するコードの生成を参照してください。

列優先のレイアウトおよび N 次元インデックス

配列レイアウトの選択は、N 次元インデックスの外観に影響します。たとえば、列優先の配列レイアウトを使用して関数 addMatrices に対するコードを生成します。

cfg.RowMajor = false;
codegen addMatrices -args {ones(2,4),ones(2,4)} -config cfg -launchreport

コード生成により、次の C コードが生成されます。

/* N-d indexing on, row-major off */
void addMatrices(double A[4][2], double B[4][2], double sum[4][2])
{
  int row;
  int col;
  for (row = 0; row < 2; row++) {
    for (col = 0; col < 4; col++) {
      sum[col][row] = A[col][row] + B[col][row];
    }
  }
}

C コードでの入力および出力行列は、元の MATLAB 行列の転置です。理由を理解するために、コンピューター メモリで配列を表現する方法について考えます。MATLAB 言語は、既定で列優先のレイアウトを使用します。ここでは、最初 (左端) の次元またはインデックスの要素はメモリ内で連続しています。C は、既定で行優先の配列レイアウトを使用します。ここでは、最後 (右端) の次元またはインデックスの要素は連続しています。元の要素の隣接性を保持するには、コード ジェネレーターは配列次元の順序を逆にしなければなりません。

たとえば、ここでは、MATLAB 行列 A を次のように定義すると仮定します。

A=reshape(1:8,2,4)

または

A =
     1     3     5     7
     2     4     6     8

MATLAB が列優先のレイアウトを使用するため、この場合、データは内部的に次の順序で格納されます。

A(:)' = 
     1     2     3     4     5     6     7     8

C コードでは、元のデータを転置しなければなりませんが、この例では AA と呼びます。

AA = {{1, 2}, {3, 4}, {5, 6}, {7, 8}};

データ要素のリストを取得するために、同じ内部ストレージの順序を使用します。言い換えれば、C 配列は 4 行 2 列でなければなりません(AA = {{1, 2, 3, 4}, {5, 6, 7, 8}} を使用して配列を 2 行 4 列として定義することで、同様のストレージの順序を取得できます。ただし、この順序を取得するには、手動でのデータの形状の変更または並べ替えが必要です)。

配列レイアウトの選択は内部データ表現のみに影響し、計算結果やアルゴリズムの結果は変わりません。生成されたコードで MATLAB 配列の直感的な外観を維持するには、N 次元インデックスを行優先の配列レイアウトと共に使用します。行優先のレイアウトが生成されたコードの効率性に影響する可能性があることに注意してください。詳細については、行優先のレイアウトのコード設計を参照してください。

コード生成のその他の考慮事項

N 次元インデックスのその他の側面について考えます。N 次元インデックスを指定する場合でも、コード ジェネレーターは、N 次元ベクトルに対して常に 1 次元配列を生成します。たとえば、MATLAB ベクトルに対してコードを生成する場合

A = zeros(1,10)

または

A = zeros(1,10,1)

結果の C/C++ 配列は、次のように格納されます。

A[10]

N 次元インデックスは、配列および構造体にも適用されます。たとえば、コードで構造体を次のように宣言する場合

x = struct('f1', ones(2,3));
coder.cstructname(x,'myStruct1');
y = struct('f2', ones(1,6,1));
coder.cstructname(y,'myStruct2');

すると、生成されたコードには構造体定義が含まれます。

typedef struct {
  double f1[2][3];
} myStruct1;
typedef struct {
  double f2[6];
} myStruct2;

N 次元配列に対する線形インデックスを行わないようにします。たとえば、コロン演算子を使用すると線形インデックスが発生します。

A(:)

線形インデックスを適用するには、コード ジェネレーターは N 次元配列を 1 次元配列にキャストしなければなりません。キャスティング演算により、コードの解析はコード ジェネレーターにとってさらに複雑になります。複雑であることによって、コード ジェネレーターのパフォーマンスの最適化機能が低下する可能性があります。

最後に、次の点に注意してください。

  • 任意のデータ型の配列に N 次元インデックスを使用できます。

  • 可変サイズ配列ではなく、固定サイズ配列のみが N 次元インデックスを使用できます。

参考

| |

関連するトピック