Main Content

分散範囲に対するループ (for-drange)

メモ

分散範囲 (drange) に対する for ループの使用は、(spmd ステートメントや通信ジョブなどの内側で) 対話型分散配列の分散次元に明示的にインデックスを付けることを目的としています。並列 for ループを含む大部分のアプリケーションでは、まず parfor ループを使用してみてください。並列 for ループ (parfor)を参照してください。

for ループの並列化

場合によっては、既に実行可能な粒度の粗いアプリケーション、つまりプログラムを起動および停止するのに必要な通信時間よりも実行時間の方が著しく長いアプリケーションが存在することがあります。ジョブおよびタスクの定義に伴うオーバーヘッドの回避が望ましい場合は、使いやすい spmd を利用できます。既存のプログラムでは独立した全データセットの処理に数時間または数日を要する場合でも、独立した計算をクラスターに分散することで、その時間を短縮できます。

たとえば、次の逐次処理コードがあるとします。

results = zeros(1, numDataSets); 
for i = 1:numDataSets
    load(['\\central\myData\dataSet' int2str(i) '.mat'])
    results(i) = processDataSet(i);
 end 
plot(1:numDataSets, results);
save \\central\myResults\today.mat results

以下の変更により、このコードは spmd で対話的に、または通信ジョブで、並列に動作します。

results = zeros(1, numDataSets, codistributor()); 
for i = drange(1:numDataSets)
    load(['\\central\myData\dataSet' int2str(i) '.mat'])
    results(i) = processDataSet(i); 
end 
res = gather(results, 1); 
if spmdIndex == 1
    plot(1:numDataSets, res);
    print -dtiff -r300 fig.tiff;
    save \\central\myResults\today.mat res
end

for drange ループ内で results にインデックスを付けるには、for 反復の長さと対話型分散配列 results の長さが一致していなければなりません。こうすると、ワーカー間での通信が不要になります。results が、元のコードを並列実行するときと同様、単に複製された配列である場合、各ワーカーは results の自己担当部分に値を代入して results の残りの部分を 0 のままに残します。最終的に results はバリアントとなり、明示的に spmdSend および spmdReceive または spmdCat を呼び出さない限り、結果の全体を 1 つ (またはすべて) のワーカーにまとめ直すことはできなくなります。

関数 load を使用する場合は、必要に応じてすべてのワーカーがデータ ファイルにアクセスできる点に注意しなければなりません。ベスト プラクティスは、共有ファイル システムでファイルへの明示的なパスを使用することです。

これに対応して、関数 save を使用する際は、一度にただ 1 つのワーカーで (共有ファイル システム上の) 特定ファイルに保存を行うよう注意しなければなりません。このため、コードを if spmdIndex == 1 内にラッピングすることを推奨します。

results は複数のワーカーに分散されるため、この例では gather を使用してデータをワーカー 1 に集約しています。

ワーカーは可視的な図をプロットできないため、関数 print でプロットの表示可能なファイルを作成します。

for-drange ループでの対話型分散配列

通信ジョブで分散範囲に対して for ループを実行すると、各ワーカーはループの担当部分を実行して、すべてのワーカーが同時に稼動します。このため、for-drange ループの実行中はワーカー間の通信が許可されません。ワーカーは対話型分散配列のうち自己担当の分割のみにアクセスできます。ワーカーが別のワーカーの対話型分散配列の部分にアクセスする必要があるループでの計算では、エラーが発生します。

この特性の説明として、以下の例を試してください。一方の for ループは機能しますが、他方の for ループは機能しません。

spmd を使用して、4 つのワーカーに分散される 2 つの対話型分散配列を作成します。1 つは単位行列に、もう 1 つはゼロ行列に設定します。

D = eye(8, 8, codistributor())
E = zeros(8, 8, codistributor())

既定では、これらの配列は列単位で分散されます。つまり、4 つのワーカーそれぞれには各配列の 2 つの列が格納されます。これらの配列を for-drange ループで使用する場合、すべての計算は各ワーカー内で完結しなければなりません。すなわち、各ワーカー内でワーカーに含まれる配列の 2 つの列に制限されている計算のみを実行できます。

たとえば、配列 E の各列を配列 D の対応する列の倍数に設定するとします。

for j = drange(1:size(D,2)); E(:,j) = j*D(:,j); end

このステートメントでは、Ej 番目の列を Dj 番目の列の j 倍に設定しています。結果として、D は主対角要素が 1 の単位行列であるのに対し、E には 123 と続くシーケンスが主対角要素に設定されます。

この演算が機能するのは、各ワーカーが 8 列中の 2 列を独立かつ同時に扱うため、計算の実行に必要な E の列と D の列すべてにアクセスできるからです。

これに対し、E の列の値を D の別の列に合わせて設定するとします。

for j = drange(1:size(D,2)); E(:,j) = j*D(:,j+1); end

このメソッドは失敗します。j が 2 のときは、D の 3 番目の列を使用して E の 2 番目の列の設定を試みることになるからです。これらの列は異なるワーカーに格納されているため、ワーカー間の通信が許可されないことを示すエラーが表示されます。

制限

対話型分散配列に対して for-drange を使用するには、以下の条件が成立していなければなりません。

  • 対話型分散配列で (2dbc ではなく) 1 次元の分散スキームを使用している。

  • 分散が既定の分割スキームに準拠している。

  • for-drange ループでインデックス付けされる変数に分散次元用の配列添字がある。

  • その他すべての添字は自由に選択できる (また、各次元の範囲全体に対する for ループから取得できる)。

配列の全要素に対してループ処理を行うには、分散の次元に対しては for-drange を、それ以外の全次元に対しては通常の for ループを使用します。以下の例は、4 つのワーカーからなる並列プールで実行される spmd ステートメント内で実行されます。

spmd
  PP = zeros(6,8,12,"codistributed");
  RR = rand(6,8,12,codistributor())
  % Default distribution: 
  %   by third dimension, evenly across 4 workers.

  for ii = 1:6
    for jj = 1:8
      for kk = drange(1:12)
        PP(ii,jj,kk) = RR(ii,jj,kk) + spmdIndex;
      end
    end
  end
end

配列の内容を表示するには、次のように入力します。

PP