通信ジョブに関する追加メモ
通信ジョブのタスク数
通信ジョブではタスクを 1 つだけ作成しますが、システムによってこのタスクはジョブを実行する各ワーカーにコピーされます。たとえば、通信ジョブが 4 つのワーカーで実行される場合、ジョブの Tasks プロパティには 4 つのタスク オブジェクトが格納されます。ジョブの Tasks プロパティ内の最初のタスクは、spmdIndex が 1 であるワーカーにより実行されるタスクに対応する (以下同様) ため、タスク オブジェクトの 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 を使用します。