メインコンテンツ

生成コードでの for ループの自動並列化

MATLAB® Coder™ は、生成される C/C++ コードにおいて、既定では Open Multiprocessing (OpenMP) ライブラリを使用して for ループを自動的に並列化します。自動並列化では、明示的および暗黙的な for ループとリダクション演算を実行する for ループの並列化がサポートされます。詳細については、Reduction Operations Supported for Automatic Parallelization of for-loopsを参照してください。

並列の C/C++ コードを生成するには、コンパイラで OpenMP ライブラリがサポートされていなければなりません。自動並列化を有効にするオプションは、coder.config 関数のすべてのビルド タイプ (MEX、DLL、LIB、および EXE) をサポートしています。

MATLAB Coder は、内部のヒューリスティックな方法を使用して、for ループを並列化すべきかどうかを判定します。

明示的および暗黙的な for ループの並列化

for ループの自動並列化では、明示的と暗黙的の両方の for ループがサポートされます。生成された C/C++ コードで並列 for ループを実行するためのCPU スレッドの最大数を指定できます。

詳細については、生成コードで並列 for ループを実行するための最大スレッド数の指定を参照してください。

明示的な for ループの並列化

明示的な for ループとは、MATLAB コードに存在している for ループのことです。自動並列化により、配列の要素単位の演算で利点が得られます。次の表は、明示的な for ループを含む MATLAB 関数と、自動並列化を使用して生成された C コードを示しています。コードを生成するには、MATLAB 関数を現在の作業ディレクトリに explicitLoop.m として保存し、codegen コマンドを実行します。

MATLAB コード

生成した C コード

% MATLAB code
function out = explicitLoop(a, b) 
    out = zeros(size(a)); 
    for i = 1:numel(a) 
        if a(i) > 1000 
            out(i) = a(i) - b(i); 
        else 
            out(i) = a(i) + b(i); 
        end 
    end 
end
% C code generation command
>> codegen explicitLoop -args {1:10000, 1:10000} -config:lib -report
#pragma omp parallel for num_threads(omp_get_max_threads()) private(d)

for (i = 0; i < 10000; i++) {
    d = a[i];
    if (d > 1000.0) {
      out[i] = d - b[i];
    } else {
      out[i] = d + b[i];
    }
}

暗黙的な for ループの並列化

暗黙的な for ループとは、MATLAB コードでは記述されていませんが、生成される C/C++ コードで for ループに変換される MATLAB 演算のことです。次の表は、暗黙的な for ループを含む MATLAB 関数と、自動並列化を使用して生成された C コードを示しています。コードを生成するには、MATLAB コードを現在の作業ディレクトリに implicitLoop.m として保存し、codegen コマンドを実行します。

MATLAB コード

生成した C コード

% MATLAB code
function [y]=  implicitLoop(in)
    a = ones(10000,1) + in;
    y = [a a];
end
% C code generation command
>> codegen implicitLoop -args {100} -config:lib -report
#pragma omp parallel for num_threads(omp_get_max_threads())

for (i = 0; i < 10000; i++) {
    y[i] = in + 1.0;
    y[i + 10000] = in + 1.0;
}

ループのバージョン管理

上記の例では、ループ境界はコンパイル時の定数です。ループ境界がコンパイル時に不明な場合、コード ジェネレーターは逐次バージョンと並列バージョンの両方の for ループを生成します。実行時のループの反復回数に応じて、より効率的なバージョンのループが実行されます。

次の表は、MATLAB 関数 loopVersion と、逐次バージョンと並列バージョンの両方の for ループを含む生成された C コードを示しています。

MATLAB コード

生成した C コード

% MATLAB code
function y = loopVersion(A, n)
   y = zeros(size(A));
   for i = 1:n
      y(i) = sin(A(i));
   end
end
% C code generation command
>> codegen loopVersion -args {1:10000, 10000} -config:lib -report
  if ((int)n < 800) {
    for (b_i = 0; b_i < i; b_i++) {
      y[b_i] = sin(A[b_i]);
    }
  } else {
#pragma omp parallel for num_threads(omp_get_max_threads())

    for (b_i = 0; b_i < i; b_i++) {
      y[b_i] = sin(A[b_i]);
    }
  }

コード生成レポートとコード洞察

上記の MATLAB 関数 explicitLoop の生成された C/C++ コードを確認するには、コード生成レポートを開きます。レポートの [コード] ペインで、for ループの横にある緑色で強調表示された行番号はコードの並列化される部分を示しています。

Generated C code for the function explicitLoop. Line numbers appear green next to the parallelized for-loop.

生成コード

生成コードで、for ループの前にある OpenMP プラグマ ステートメントは for ループの並列化を示しています。

void explicitLoop(const double a[10000], const double b[10000],
                  double out[10000])
{
  double d;
  int i;
  if (!isInitialized_explicitLoop) {
    explicitLoop_initialize();
  }
#pragma omp parallel for num_threads(omp_get_max_threads()) private(d)

  for (i = 0; i < 10000; i++) {
    d = a[i];
    if (d > 1000.0) {
      out[i] = d - b[i];
    } else {
      out[i] = d + b[i];
    }
  }
}

コード洞察

[コード洞察] タブの [自動並列化] で、生成コードにおいて並列化またはバージョン化されていない for ループについての詳細情報を確認できます。

たとえば、入力引数に指定するサイズを小さくして、前に定義した explicitLoop 関数のコードを再度生成します。

>> codegen explicitLoop -args {1:100, 1:100} -config:lib -report

この場合、実行時にパフォーマンス上の利点は得られないため、for ループは並列化されません。このようなコードの洞察を表示するには、コード生成レポートを開いて [コード洞察]、[自動並列化] をクリックします。

Code Insights tab showing information about for-loops that are not parallelized.

for ループの並列化の制御

逐次実行の方が優れたパフォーマンスが得られる場合は、for ループの自動並列化を無効にできます。

すべての for ループの並列化を無効化

parforcoder.loop.parallelize("loopID") が続くループの並列化は無効にできません。

すべての for ループの自動並列化を無効にするには、次のようにします。

  • MATLAB Coder アプリで、[詳細設定]、[速度] ペインの [自動並列化を有効にする] 設定をオフにする。

  • MATLAB コマンド ウィンドウで、コード構成オプション EnableAutoParallelizationfalse に設定する。

特定の for ループの並列化を無効化

特定の for ループを並列化しないようにするには、MATLAB コードでそのループの直前に coder.loop.parallelize("never") を配置します。これは EnableAutoParallelization 設定をオーバーライドします。

たとえば、コード ジェネレーターは次のループを並列化しません。

coder.loop.parallelize("never");
for i = 1:n
    y(i) = y(i)*sin(i);
end

特定の for ループの並列化を有効化

特定の for ループを並列化するには、MATLAB コードでその for ループの直前に coder.loop.parallelize("loopID") を配置します。これは EnableAutoParallelization 設定をオーバーライドします。

たとえば、次の for ループは生成コードで常に並列化されます。

coder.loop.parallelize("i");
for i = 1:100
    out1(i) = out1(i)*i;
end

詳細については、coder.loop.parallelize を参照してください。

使用上の注意および制限

  • 入れ子にされた for ループの場合、MATLAB Coder は最も外側の for ループを並列化し、最も内側の for ループをベクトル化します。

  • parfor ループを含む for ループは並列化されません。

  • 自動並列化では、本体に永続変数または永続変数にアクセスする関数の呼び出しが含まれている for ループはサポートされません。

  • 自動並列化では、外部関数の呼び出しを含むコード内の for ループはサポートされません。

  • while ループは並列化されません。

  • シングル コアのハードウェア ターゲットまたは NumberOfCpuThreads1 に設定されたハードウェア ターゲットは自動的に並列化されません。

  • OpenMP がターゲット ハードウェアでサポートされていない場合、または coder.CodeConfig オブジェクトのプロパティEnableOpenMPfalse に設定されている場合、いずれの for ループも並列化されません。

  • 単一レベルの for ループについて、ベクトル化と並列化が可能な場合はベクトル化されます。

参考

| | | | |

トピック