Main Content

リダクション変数

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

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

X = X + expr X = expr + X
X = X - expr リダクション代入の要件の「リダクション代入の結合性」を参照してください。
X = X .* expr X = expr .* X
X = X * expr X = expr * X
X = X & expr X = expr & X
X = X | expr X = 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 Xparfori = 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ループは有効です。

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

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

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

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

無効 有効
a.x = 0parfori = 1:10 a.x = a.x + 1;end
tmpx = 0parfori = 1:10 tmpx = tmpx + 1;enda.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;parfori = 1:n a = f(a,i);% loop body continuedf = @times;% Affects fend
f = @(x,k)x * k;parfori = 1:n a = f(a,i);% loop body continuedend

演算子&&および演算子||リダクション変数の表にリストされていません。&&||を除く 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と等価であるという数学の法則に従わなければなりません。

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

この例では、ループでの計算を実行し、最大値とそれに対応するループ インデックスを格納します。独自のリダクション関数とparforループを使用して、コードを高速化することができます。反復ごとに、計算の値とループ インデックスを 2 要素の行ベクトルに格納します。カスタム リダクション関数を使用して、このベクトルを格納されたベクトルと比較します。計算からの値が格納された値より大きい場合は、古いベクトルを新しいベクトルに置き換えます。

リダクション関数compareValueを作成します。この関数は、入力としてvalueAndIndexAvalueAndIndexBの 2 つのベクトルを取ります。各ベクトルには値とインデックスが含まれています。リダクション関数compareValueは、値 (最初の要素) が最も大きいベクトルを返します。

functionv = compareValue(valueAndIndexA, valueAndIndexB) valueA = valueAndIndexA(1); valueB = valueAndIndexB(1);ifvalueA > valueB v = valueAndIndexA;elsev = valueAndIndexB;endend

すべて 0 からなる 1 行 2 列のベクトルmaxValueAndIndexを作成します。

maxValueAndIndex = [0 0];
parforループを実行します。各反復で、randを使用して乱数値を作成します。その後、リダクション関数compareValueを使用して、maxValueAndIndexを乱数値およびループ インデックスと比較します。結果をmaxValueAndIndexとして格納する場合、maxValueAndIndexをリダクション変数として使用します。

parforii = 1:100% Simulate some actual computationthisValueAndIndex = [rand() ii];% Compare valuemaxValueAndIndex = compareValue(maxValueAndIndex, thisValueAndIndex);end

parforループの実行が終了した後、リダクション変数maxValueAndIndexはクライアントで使用可能になります。最初の要素はparforループで計算された最大の乱数値で、2 番目の要素はそれに対応するループ インデックスです。

maxValueAndIndex
maxValueAndIndex = 0.9706 89.0000

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

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;parfori=1:10 X = X + 1 * 2;end

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

X = 0;parfori=1:10 X = X * 1 + 2;end

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

関連するトピック