Main Content

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

通信ジョブに関する追加メモ

通信ジョブのタスク数

通信ジョブではタスクを 1 つだけ作成しますが、システムによってこのタスクはジョブを実行する各ワーカーにコピーされます。たとえば、通信ジョブが 4 つのワーカーで実行される場合、ジョブの Tasks プロパティには 4 つのタスク オブジェクトが格納されます。ジョブの Tasks プロパティ内の最初のタスクは、spmdIndex1 であるワーカーにより実行されるタスクに対応する (以下同様) ため、タスク オブジェクトの ID プロパティおよびそのタスクを実行したワーカーの spmdIndex は同じ値になります。したがって、関数 fetchOutputs により返される結果の順序は、spmdIndex の値とジョブの Tasks プロパティ内のタスクの順序に対応します。

デッドロックなどの依存関係エラーの回避

通信ジョブのために特定のワーカーで実行されているコードは、別のワーカーで対応コードが実行されるまでブロックされる場合があるため、通信ジョブではデッドロックが発生する可能性があります。デッドロックが最も発生しやすいのは、ワーカー間でデータを転送する場合や、コードを if ステートメントの spmdIndex に依存させている場合です。いくつかの例により、一般的な落とし穴について説明します。

対話型分散配列 D があり、関数 gather を使用して配列全体を単一のワーカーのワークスペースにまとめるとします。

if spmdIndex == 1
    assembled = gather(D);
end

これが失敗する理由は、関数 gather によって、配列が分散されるすべてのワーカー間で通信が要求されるためです。if ステートメントで実行が 1 つのワーカーに制限される場合、関数の実行に必要な他のワーカーはこのステートメントを実行していません。代替方法として、次のように gather 自体を使用してデータを単一ワーカーのワークスペースに集めることができます。 assembled = gather(D, 1).

もう 1 つの例として、各ワーカーのデータを (spmdIndex が 1 つ上として定義される) 右側にある次のワーカーに転送する場合を考えます。まず、各ワーカーに対し、左右のワーカーを定義します。

from_worker_left = mod(spmdIndex - 2, spmdSize) + 1;
to_worker_right  = mod(spmdIndex, spmdSize) + 1;

次に、この輪に沿ってデータを渡すよう試みます。

spmdSend (outdata, to_worker_right);
indata = spmdReceive(from_worker_left);

このコードは失敗する可能性がありますが、その理由は、転送するデータのサイズ次第で、受信側のワーカーが関数 spmdReceive を実行するまで関数 spmdSend が対応するワーカーでの実行をブロックする場合があるためです。この場合、すべてのワーカーが同時に送信を試みますが、spmdSend によるブロックのため、どのワーカーも受信しようとしていません。つまり、どのワーカーも spmdSend ステートメントのステップでブロックされているため、spmdReceive ステートメントに到達できていません。この問題を回避するには、関数 spmdSendReceive を使用します。