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
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
ループのみを展開しました。