Main Content

可変サイズ データのコードの高速化

可変サイズ データは実行時にサイズが変わる可能性のあるデータです。MATLAB® では、コード生成に対して制限付きと制限なしの可変サイズ データがサポートされています。"制限付き可変サイズ データ" には固定の上限が設定されています。このデータはスタックで静的に割り当てることも、ヒープで動的に割り当てることもできます。"制限なしの可変サイズ データ" には上限が設定されていません。このデータはヒープで割り当てなければなりません。既定では、MEX および C/C++ コードの生成において、可変サイズ データのサポートは有効で、構成可能なしきい値を超えるサイズの可変サイズ配列への動的なメモリの割り当てが有効になっています。

可変サイズ データのサポートの無効化

既定では、MEX および C/C++ コードの高速化において、可変サイズ データのサポートは有効になっています。コマンド ラインで可変サイズの設定を変更できます。

  1. コード生成の構成オブジェクトを作成します。

    cfg = coder.mexconfig;

  2. EnableVariableSizing オプションを次のように設定します。

    cfg.EnableVariableSizing = false;

  3. -config オプションを使用して、構成オブジェクトを fiaccel に渡します。

    fiaccel -config cfg foo
    

動的メモリ割り当ての制御

既定では、可変サイズ配列への動的なメモリの割り当てが有効になっていますが、配列サイズは構成可能なしきい値を超えています。可変サイズ データのサポートを無効にすると、動的メモリ割り当ても無効になります。コマンド ラインで動的メモリ割り当ての設定を変更できます。

  1. コード高速化の構成オブジェクトを作成します。たとえば、MEX 関数の場合は、以下のようになります。

    mexcfg = coder.mexconfig;

  2. EnableDynamicMemoryAllocation オプションを次のように設定します。

    設定アクション
    mexcfg.EnableDynamicMemoryAllocation=false;
    動的メモリ割り当てが無効になります。可変サイズ データはすべてスタックで静的に割り当てられます。
    mexcfg.EnableDynamicMemoryAllocation=true;
    DynamicMemoryAllocationThreshold パラメーターを使用して指定された値以上のサイズ (バイト単位) のすべての可変サイズ配列に対して動的メモリ割り当てが有効になります。このしきい値より小さいサイズの可変サイズ配列は、スタックで割り当てられます。

  3. オプションとして、EnableDynamicMemoryAllocationtrue に設定する場合は、DynamicMemoryAllocationThreshold を構成してメモリ割り当てを調整します。

  4. -config オプションを使用して、構成オブジェクトを fiaccel に渡します。

    fiaccel -config mexcfg foo
    

可変サイズ データによる MATLAB 関数のコードの高速化

MEX コードを生成する基本ワークフローを以下に示します。

  1. MATLAB エディターで、コンパイル命令 %#codegen を関数の最上部に追加します。

    この命令は、以下を実行します。

    • MATLAB アルゴリズムのコードを生成することを示す。

    • コード生成中に潜在的なエラーを検出するために MATLAB コード アナライザーでのチェックをオンにする。

  2. コード アナライザーで検出された問題を解決します。

    場合によっては、コードでデータに固定サイズが割り当てられているときに、ループでの割り当てや連結によって後からデータが大きくなると、MATLAB コード アナライザーが警告を表示することがあります。実行時にデータのサイズが変化することが想定されている場合は、これらの警告を無視できます。

  3. fiaccel を使用して MEX 関数を生成します。以下のコマンド ライン オプションを使用します。

    • -args {coder.typeof...}: 可変サイズ入力がある場合

    • -report: コード生成レポートを生成する場合

    以下に例を示します。

    fiaccel -report foo -args {coder.typeof(0,[2 4],1)}
    
    このコマンドは coder.typeof を使用して関数 foo への可変サイズ入力を 1 つ指定します。最初の引数 0 は、入力データ型 (double) と実数/複素数 (real) を示します。2 番目の引数 [2 4] は、サイズを示す 2 次元の行列です。3 番目の引数 1 は、入力が可変サイズであることを示します。最初の次元の上限は 2 で、2 番目の次元の上限は 4 です。

    メモ

    コンパイル中に fiaccel は定義後にサイズが変化した変数と構造体フィールドを検出し、これらが発生した場合はエラーとして報告します。さらに、fiaccel は実行時チェックを実行し、データが上限を超えた場合はエラーを生成します。

  4. サイズ不一致エラーを解決します。

    原因:解決方法:詳細:
    データのサイズがロックされた後にサイズを変更しようとした。データを可変サイズとして宣言します。サイズ不一致エラーの診断と修正を参照してください。
  5. 上限エラーを解決します。

    原因:解決方法:詳細:
    MATLAB が上限を判断または計算できない。上限を指定します。可変サイズ配列の上限の指定サイズ不一致エラーの診断と修正を参照してください。
    MATLAB が制限なしの可変サイズ データの上限を計算しようとした。制限なしのデータの場合は、動的メモリ割り当てを有効にします。 動的メモリ割り当ての制御を参照してください。
  6. 関数 fiaccel を使用して C/C++ コードを生成します。

ループ内でベクトルを拡張する MATLAB 関数のコードの高速化

MATLAB 関数 uniquetol について

この例は、関数 uniquetol を使用します。この関数は、入力ベクトル A のバージョンであるベクトル B で返されます。ここで、各要素はそれぞれの許容誤差 tol 内で一意であるとします。ベクトル B では、すべての ij に対して abs(B(i) - B(j)) > tol となります。まず、入力ベクトル A は最大 100 までの要素を格納できると想定します。

function B = uniquetol(A, tol)
A = sort(A);
B = A(1);
k = 1;
for i = 2:length(A)
   if abs(A(k) - A(i)) > tol
      B = [B A(i)];
      k = i;
   end
end

手順 1: コード生成のコンパイル命令の追加

コンパイル命令 %#codegen を関数の最上部に追加します。

function B = uniquetol(A, tol) %#codegen
A = sort(A);
B = A(1);
k = 1;
for i = 2:length(A)
   if abs(A(k) - A(i)) > tol
      B = [B A(i)];
      k = i;
   end
end

手順 2: コード アナライザーで検出された問題の解決

コード アナライザーは変数 B のサイズが for ループ内で変化する可能性を検出します。これにより、次の警告が表示されます。

The variable 'B' appears to change size on every loop iteration.
Consider preallocating for speed.

この関数では、ベクトル B はベクトル A から値を追加しながらサイズを拡大します。したがって、この警告は無視できます。

手順 3: MEX コードの生成

MEX コードを生成するには、関数 fiaccel を使用します。

  1. uniquetol の MEX 関数を生成します。

    T = numerictype(1, 16, 15);
    fiaccel -report uniquetol -args {coder.typeof(fi(0,T),[1 100],1),coder.typeof(fi(0,T))}
    

     これらのコマンド ライン オプションの意味

    このコマンドを実行すると、以下のコンパイラ エラーが生成されます。

    ??? Size mismatch (size [1 x 1] ~= size [1 x 2]).
    The size to the left is the size 
    of the left-hand side of the assignment.
    
  2. エラー レポートを開き、[変数] タブを選択します。

エラーには、代入ステートメント B = [B A(i)]; の左辺と右辺のサイズの不一致が示されています。代入 B = A(1) により、B のサイズが固定サイズのスカラー (1 行 1 列) に決定されます。したがって、[B A(i)] の連結により、1 行 2 列のベクトルが作成されます。

手順 4: サイズ不一致エラーの解決

このエラーを解決するには、B を可変サイズのベクトルとして宣言します。

  1. このステートメントを関数 uniquetol に追加します。

    coder.varsize('B');

    これは B が使用される (読み取られる) 前に記述しなければなりません。以下に例を示します。

    function B = uniquetol(A, tol) %#codegen
    A = sort(A);
    
    coder.varsize('B');
    
    B = A(1);
    k = 1;
    for i = 2:length(A)
       if abs(A(k) - A(i)) > tol
          B = [B A(i)]; 
          k = i;
       end
    end

    関数 coder.varsizeuniquetolB の各インスタンスを可変サイズとして宣言します。

  2. 同じコマンドを使用してコードを再度生成します。

    fiaccel -report uniquetol -args {coder.typeof(fi(0,T),[1 100],1),coder.typeof(fi(0,T))}

    現在のフォルダーで、fiacceluniquetol_mex という uniquetol の MEX 関数を生成し、コード生成レポートへのリンクを提供します。

  3. [レポートの表示] リンクをクリックします。

  4. コード生成レポートで [変数] タブを選択します。

    変数 B のサイズは 1x:? で、上限のない可変サイズであることが示されます。

手順 5: MEX 関数と元のコードの実行速度の比較

同じ反復回数のループに対して同じ入力を使用して、元の MATLAB アルゴリズムと MEX 関数を実行し、実行速度を比較します。

  1. uniquetol の MATLAB 関数と MEX 関数に渡す入力を正しいクラス、実数/複素数およびサイズを使用して作成します。

    x = fi(rand(1,90), T);
    tol = fi(0, T);
    
  2. 元の関数 uniquetol をループで実行して、ループを 10 回反復するのにかかる時間を測定します。

    tic; for k=1:10, b = uniquetol(x,tol); end; tSim=toc
    
  3. 同じ反復回数のループに対して同じ入力を使用して、生成された MEX 関数を実行します。

    tic; for k=1:10, b = uniquetol_mex(x,tol); end; tSim_mex=toc
    
  4. 実行時間を比較します。

    r = tSim/tSim_mex
    

    この例は、fiaccel を使用して MEX 関数を生成すると、固定小数点アルゴリズムの実行が大幅に速くなることを示しています。