Main Content

for ループおよび parfor ループの展開

for ループまたは parfor ループを展開するとき、コード ジェネレーターは生成されたコード内でループを作成するのではなく、各反復でループ本体のコピーを作成します。小規模で詰まったループの場合、展開によってパフォーマンスが向上します。ただし、大規模なループの場合、展開によってコード生成時間が大幅に増加し、非効率なコードが生成される可能性があります。

coder.unroll を使用して for ループ展開を強制的に実行

コード ジェネレーターはヒューリスティックな方法を使用して for ループを展開するタイミングを判断します。ループ展開を強制的に行うには、coder.unroll を使用します。これは、coder.unroll の直後の for ループにのみ影響します。次に例を示します。

function z = call_myloop()
%#codegen
z = myloop(5);
end

function b = myloop(n)
b = zeros(1,n);
coder.unroll();
for i = 1:n
    b(i)=i+n;
end
end

for ループに対して生成されたコードは、次のとおりです。

  z[0] = 6.0;
  z[1] = 7.0;
  z[2] = 8.0;
  z[3] = 9.0;
  z[4] = 10.0;

for ループが展開されるタイミングを制御するには、coder.unroll flag 引数を使用します。たとえば、反復の回数が 10 回未満のときにのみループを展開します。

function z = call_myloop()
%#codegen
z = myloop(5);
end

function b = myloop(n)
unroll_flag = n < 10;
b = zeros(1,n);
coder.unroll(unroll_flag);
for i = 1:n
    b(i)=i+n;
end
end

for ループを展開するには、コード ジェネレーターは for ループの範囲を決定できなければなりません。たとえば、コード生成時に n の値が不明なため、次のコードのコード生成は失敗します。

function b = myloop(n)
b = zeros(1,n);
coder.unroll();
for i = 1:n
    b(i)=i+n;
end
end

MATLAB コード内のすべての for ループと parfor ループに対するループ展開しきい値の設定

for ループの前に coder.unroll がない場合、コード ジェネレーターはループ展開のしきい値を使用して自動的にループを展開するかどうかを決定します。ループ反復回数がしきい値未満の場合、コード ジェネレーターはループを展開します。反復回数がしきい値以上の場合、コード ジェネレーターは for ループを生成します。ループ展開のしきい値を使用することで、parfor ループも展開できます。

このしきい値の既定値は 5 です。このしきい値を変更して、ループ展開を微調整できます。しきい値を変更するには、次を実行します。

  • スタンドアロン コード生成の構成オブジェクトで (coder.CodeConfig または coder.EmbeddedCodeConfig)、LoopUnrollThreshold プロパティを設定します。

  • MATLAB® Coder™ アプリの [速度] タブで、[ループ展開のしきい値] を設定します。

coder.unroll 命令とは異なり、しきい値は MATLAB コード内のすべての for ループに適用されます。しきい値は、コード生成中に生成された一部の for ループにも適用できます。

個々のループでは、coder.unroll 命令はループ展開の最適化より優先されます。

単純な for ループの展開

次の関数を考えてみます。

function [x,y] = call_myloops()
%#codegen
x = myloop1(5);
y = myloop2(5);
end

function b = myloop1(n)
b = zeros(1,n);
for i = 1:n
    b(i)=i+n;
end
end

function b = myloop2(n)
b = zeros(1,n);
for i = 1:n
    b(i)=i*n;
end
end

ループ展開のしきい値の値を 6 に設定してから、スタティック ライブラリを生成するには、次を実行します。

cfg = coder.CodeConfig;
cfg.LoopUnrollThreshold = 6;
codegen call_myloops -config cfg

これは for ループに対して生成されたコードです。コード ジェネレーターは両方の for ループを展開しました。

  x[0] = 6.0;
  y[0] = 5.0;
  x[1] = 7.0;
  y[1] = 10.0;
  x[2] = 8.0;
  y[2] = 15.0;
  x[3] = 9.0;
  y[3] = 20.0;
  x[4] = 10.0;
  y[4] = 25.0;

入れ子にされた for ループの展開

MATLAB コードには入れ子にされた 2 つの for ループがあるとします。

  • 内側のループの反復回数がしきい値未満である場合、コード ジェネレーターは最初に内側のループを展開します。次に、2 つのループの反復回数の積もしきい値未満である場合は、コード ジェネレーターは外側のループを展開します。そうでない場合、コード ジェネレーターは外側の for ループを生成します。

  • 内側のループの反復回数がしきい値以上である場合、コード ジェネレーターは両方の for ループを生成します。

この動作は、複数の入れ子にされた for ループに一般化されています。

入れ子にされた 2 つの for ループを使用する関数 nestedloops_1 について考えます。

function y = nestedloops_1
%#codegen
y = zeros(2,2);
for i = 1:2
    for j = 1:2
        y(i,j) = i+j;
    end
end
end

ループ展開のしきい値が既定値の 5 に設定された、nestedloops_1 のコードを生成します。for ループに対して生成されたコードは、次のとおりです。2 つのループの反復回数の積がしきい値より小さい 4 であるため、コード ジェネレーターは両方の for ループを展開しました。

  y[0] = 2.0;
  y[2] = 3.0;
  y[1] = 3.0;
  y[3] = 4.0;

ここで、ループ展開のしきい値が既定値の 5 に設定された、関数 nestedloops_2 のコードを生成します。

function y = nestedloops_2
%#codegen
y = zeros(3,2);
for i = 1:3
    for j = 1:2
        y(i,j) = i+j;
    end
end
end

内側のループの反復回数はしきい値より小さくなります。コード ジェネレーターは内側のループを展開します。しかし、2 つのループの反復回数の積は、しきい値より大きい 6 です。そのため、コード ジェネレーターは外側の for ループのコードを生成します。for ループに対して生成されたコードは、次のとおりです。

  for (i = 0; i < 3; i++) {
    y[i] = (double)i + 2.0;
    y[i + 3] = ((double)i + 1.0) + 2.0;
  }

parfor ループの展開

次の MATLAB 関数を考えてみます。

function [x,y] = parallel_loops()
%#codegen
x = myloop1(5);
y = myloop2(6);
end

function b = myloop1(n)
b = zeros(1,n);
parfor (i = 1:n)
    b(i)=i+n;
end
end

function b = myloop2(n)
b = zeros(1,n);
parfor (i = 1:n)
    b(i)=i*n;
end
end
ループ展開のしきい値の値を 6 に設定してから、スタティック ライブラリを生成します。
cfg = coder.CodeConfig;
cfg.LoopUnrollThreshold = 6;
codegen parallel_loops -config cfg
生成されたコードは次のとおりです。

static void myloop1(double b[5])
{
  b[0] = 6.0;
  b[1] = 7.0;
  b[2] = 8.0;
  b[3] = 9.0;
  b[4] = 10.0;
}
static void myloop2(double b[6])
{ int i;
#pragma omp parallel for num_threads(omp_get_max_threads())

  for (i = 0; i < 6; i++) {
    b[i] = ((double)i + 1.0) * 6.0;
  }}
void parallel_loops(double x[5], double y[6])
{
  if (!isInitialized_parallel_loops) {
    parallel_loops_initialize();
  }
  myloop1(x);
  myloop2(y);}

コード ジェネレーターは、反復回数がしきい値未満の 5 回である parfor ループのみを展開しました。

参考

関連するトピック