Main Content

このページの翻訳は最新ではありません。ここをクリックして、英語の最新版を参照してください。

リダクション変数

MATLAB® は、ループ反復が独立していなければならないというルールに対し、リダクションと呼ばれる重要な例外をサポートしています。"リダクション変数" は、すべての反復に全体として依存し、かつ反復順序とは独立した値を累積します。MATLAB では、parfor ループ内でリダクション変数を使用できます。

リダクション変数は、以下のいずれにも見られるように、代入ステートメントの両辺に配置されます。ここで、expr は MATLAB 式です。

X = X + exprX = expr + X
X = X - expr リダクション代入の要件の「リダクション代入の結合性」を参照してください。
X = X .* exprX = expr .* X
X = X * exprX = expr * X
X = X & exprX = expr & X
X = X | exprX = expr | X
X = [X, expr]X = [expr, X]
X = [X; expr]X = [expr; X]
X = min(X, expr)X = min(expr, X)
X = max(X, expr)X = max(expr, X)
X = union(X, expr)X = union(expr, X)
X = intersect(X, expr)X = intersect(expr, X)

この表にリストされている利用可能な各ステートメントは、"リダクション代入" と呼ばれます。定義により、リダクション変数はこのタイプの代入にのみ配置できます。

リダクション代入の一般的な形式は次のとおりです。

X = f(X, expr)X = f(expr, X)

次の例は、リダクション変数 X の典型的な使用方法を示しています。

X = 0;            % Do some initialization of X
parfor i = 1:n
    X = X + d(i);
end

このループは次と等価です。ここで、それぞれの d(i) は異なる反復で計算されます。

X = X + d(1) + ... + d(n)

標準的な for ループの場合、変数 X はループに入る前に、またはループの前回の反復からその値を取得します。しかし、この把握の仕方は parfor ループには適用されません。

parfor ループでは、X の値がクライアントからワーカーへ、またワーカーからクライアントへ伝送されることはありません。代わりに、d(i) の加算は各ワーカー内で実行され、i の範囲はそのワーカーで実行される 1:n のサブセットとなります。その後、結果がクライアントに返送され、クライアントがワーカーの部分和を X に加算します。したがって、ワーカーが加算の一部を実行し、クライアントが残りを行うことになります。

必須および推奨ガイドラインに関するメモ

必須というラベルの付いたガイドラインと制限に parfor コードが従っていない場合は、エラーが発生します。MATLAB はコードの読み取り時こうしたエラーの一部を、コードの実行時に他のエラーを検出します。これらのエラーにはそれぞれ、必須 (静的) または必須 (動的) のラベルが付けられます。エラーにつながらないガイドラインには推奨というラベルを付けています。MATLAB コード アナライザーを使用して、parfor ループをガイドラインに準拠させることができます。

リダクション変数の基本ルール

次の要件は、与えられた変数に関連付けられたリダクション代入をさらに規定しています。

必須 (静的): すべてのリダクション変数に対して、その変数のすべてのリダクション代入で同じリダクション関数またはリダクション演算を使用しなければなりません。

左側の parfor ループはリダクション代入の 1 つのインスタンスに + を使用し、別のインスタンスに [,] を使用しているため、無効です。右側の parfor ループは有効です。

無効有効
parfor i = 1:n
    if testLevel(k)
        A = A + i;
    else
        A = [A, 4+i];
    end
    % loop body continued
end
parfor i = 1:n
    if testLevel(k)
        A = A + i;
    else
        A = A + i + 5*k;
    end
    % loop body continued
end
必須 (静的): リダクション代入で *[,] または [;] を使用する場合、どのリダクション代入でも一貫して X を最初か 2 番目の引数として指定しなければなりません。

左側の parfor ループは無効です。連結内での項目の順序がループ内で一貫していないためです。右側の parfor ループは有効です。

無効有効
parfor i = 1:n
    if testLevel(k)
        A = [A, 4+i];
    else
        A = [r(i), A];
    end
    % loop body continued
end
parfor i = 1:n
    if testLevel(k)
        A = [A, 4+i];
    else
        A = [A, r(i)];
    end
    % loop body continued
end
必須 (静的): リダクション変数にインデックスを付けたり、添字を使用したりすることはできません。

左側のコードは無効です。a にインデックスを付けようとしており、MATLAB はそれをリダクション変数として分類できないためです。これを修正するために、右側のコードではインデックスのない変数を使用しています。

無効有効
a.x = 0
parfor i = 1:10
    a.x = a.x + 1;
end
tmpx = 0
parfor i = 1:10
    tmpx = tmpx + 1;
end
a.x = tmpx;

リダクション代入の要件

リダクション代入。リダクション変数の表に示す特定形式のリダクション代入の他には、唯一の (より一般的な) 形式のリダクション代入は以下のようになります。

X = f(X, expr)X = f(expr, X)
必須 (静的): f には関数または変数のいずれかを指定できます。f が変数の場合、parfor 本体で f を変更することはできません (つまり、ブロードキャスト変数であるということです)。

f が変数の場合、実際的には、その実行時の値は関数ハンドルになります。ただし、右辺が評価可能である限り、結果の値は X に格納されます。

左側の parfor ループは正しく実行されません。ステートメント f = @times により f が一時変数として分類されるためです。したがって、f は各反復の開始時にクリアされます。右側の parfor ループは、ループ内で f に代入を行っていないため適正です。

無効有効
f = @(x,k)x * k;
parfor i = 1:n
    a = f(a,i);
    % loop body continued
    f = @times;  % Affects f
end
f = @(x,k)x * k;
parfor i = 1:n
    a = f(a,i);
    % loop body continued
end

演算子 && および演算子 ||リダクション変数の表にリストされていません。&&|| を除く MATLAB のすべての行列演算には、対応する関数 f があります。たとえば、u op vf(u,v) と等価です。&&|| の場合、このような関数は作成できません。u&&vu||v には、v を評価する場合としない場合があるためです。しかし、f(u,v) は "常に" v を評価してから f を呼び出します。そのため、&&||parfor ループで利用可能なリダクション代入の表から除外されています。

各リダクション代入には、関数 f が関連付けられています。parfor ステートメントの確定的動作を確保する f のプロパティについては、以下の節で説明します。

リダクション代入の結合性。リダクション変数の定義で使用される関数 f について、次の手法を推奨します。ただし、このルールを順守しなくてもエラーは発生しません。このため、コードでこの推奨を順守するかどうかはユーザーに任せられています。

推奨: parfor ループの確定的動作を得るには、リダクション関数 f が結合的でなければなりません。

結合的にするには、関数 f はすべての abc について次を満たさなければなりません。

f(a,f(b,c)) = f(f(a,b),c)

リダクション変数を含め、変数の分類ルールは純粋に構文的なものです。こうしたルールでは、指定した f が本当に結合的かどうかを判断できません。結合性が仮定されますが、このルールに違反した場合、ループを実行するたびに異なった結果が返される可能性があります。

メモ

数学的な実数の加法は結合的です。ただし、浮動小数点数の加法は近似的にのみ結合的です。この parfor ステートメントの実行が異なれば、異なる丸め誤差を持つ X の値が出力される可能性があります。この並列処理の代価は回避できません。

たとえば、左側のステートメントが 1 を出力するのに対し、右側のステートメントは 1 + eps を返します。

(1 + eps/2) + eps/2           1 + (eps/2 + eps/2)

マイナス演算子 (-) を除き、リダクション変数の表に挙げられたすべての特殊ケースには、対応する (近似的に) 結合的な関数があります。MATLAB は代入 X = X - expr を、X = X + (-expr) を使用して計算します (このため、技術的には、このリダクション代入を計算する関数は plus であり、minus ではありません)。しかし、代入 X = expr - X は結合関数を使用して記述できず、この理由で表から除外されています。

リダクション代入の可換性。 +.*minmaxintersect および union を含む一部の結合関数は、可換でもあります。つまり、すべての a および b について次を満たします。

f(a,b) = f(b,a)

非可換の関数には、* (両方の次元のサイズが 1 より大きい行列では乗算が可換ではないため)、[,] および [;] が含まれます。非可換のため、これらの関数では引数の順序に一貫性が必要です。実践的な問題として、関数が可換的かつ結合的で、parfor が可換性を生かすよう最適化されている場合は、より効率的なアルゴリズムが可能となります。

推奨: *[,] および [;] の場合を除いて、リダクション代入の関数 f は可換でなければなりません。f が可換的ではない場合、異なるループの実行により異なる回答が返される可能性があります。

リダクションに使用される関数内で可換性の制限に違反すると、たとえエラーが発生なくとも、予期しない動作につながる可能性があります。

f は、それが既知の非可換な組み込み関数でない限りは可換であると仮定されます。現時点では、parfor にユーザー定義の非可換的関数を指定する方法はありません。

推奨: parfor ループのリダクション代入で +*.*[,] または [;] を使用する場合、そのオーバーロードは結合的でなければなりません。
推奨: +.*union または intersect のオーバーロードは可換でなければなりません。

同様に、X = X - expr の特殊な扱いのため、以下を推奨します。

推奨: マイナス演算子 (-) のオーバーロードでは、X - (y + z)(X - y) - z と等価であるという数学の法則に従わなければなりません。

カスタム リダクション関数の使用

ループの各反復で何らかの計算を行い、ループのどの反復が最大値を出力するかを調べることに関心があるとします。このリダクションの演習では、1 つのループにおける複数の反復にわたって累積を行います。使用するリダクション関数では、すべての反復を比較した後に最大値が決められるようになるまで、反復結果を比較しなければなりません。

まずリダクション関数自体について考察します。ある反復結果を別の反復結果と比較するため、関数には入力として現在の結果と、これまでの他の反復から得た既知の最大値が必要です。2 つの入力は、それぞれが反復の結果と反復番号を含むベクトルです。

function mc = comparemax(A, B)
    % Custom reduction function for 2-element vector input
    
    if A(1) >= B(1) % Compare the two input data values
        mc = A;     % Return the vector with the larger result
    else
        mc = B;
    end
end

ループ内の各反復は、リダクション関数 (comparemax) を呼び出して 1 組の 2 要素ベクトルを渡します。

  • 累積された最大値とその反復インデックス (リダクション変数 cummax)

  • 反復の値とインデックス

現在の反復のデータ値が cummmax の最大値よりも大きい場合、関数は新しい値とその反復番号を成分とするベクトルを返します。そうでない場合は、既存の最大値とその反復番号を返します。

各反復はリダクション関数 comparemax を呼び出し、それ自体のデータ [dat i]cummax で既に累積されたデータと比較します。このループについて、次のコードを試してください。

% First element of cummax is maximum data value
% Second element of cummax is where (iteration) maximum occurs
cummax = [0 0];  % Initialize reduction variable
parfor ii = 1:100
    dat = rand(); % Simulate some actual computation
    cummax = comparemax(cummax, [dat ii]);
end
disp(cummax);

リダクション演算子の連結

X = expr op X または X = X op expr の形式の代入が、かっこで囲んだ代入 X = (expr) op X または X = X op (expr) とそれぞれ等価であるとき、MATLAB ではそれらがリダクション ステートメントとして分類されます。X は変数、op はリダクション演算子、そして expr は二項リダクション演算子を 1 つ以上含む式です。その結果、MATLAB の演算子の優先順位のルールにより、MATLAB では演算子が連結する X = expr op1 X op2 expr2 ... の形式の代入の一部が、parfor ループ内のリダクション ステートメントとして分類されない可能性があります。

次の例では、代入が X = X + (1 * 2) と等価であるため、MATLAB は X をリダクション変数として分類します。

X = 0;
parfor i=1:10
    X = X + 1 * 2;
end

次の例では、X = (X * 1) + 2 と等価である代入が X = (expr) op X または X = X op (expr) の形式ではないため、MATLAB は X を一時変数として分類します。

X = 0;
parfor i=1:10
    X = X * 1 + 2;
end

ベスト プラクティスとして、連結するリダクション代入では、かっこを使用して明示的に演算子の優先順位を指定します。

関連するトピック