複数のデータセットでの単一プログラムの実行
はじめに
Single Program Multiple Data (SPMD) 言語構成を使用すると、逐次プログラミングと並列プログラミングをシームレスにインターリーブできます。spmd ステートメントには、複数のワーカーで同時に実行されるコード ブロックを定義できます。ワーカーの spmd ステートメント内で割り当てられた変数の値には、Composite (複合) オブジェクトを介した参照によってクライアントから直接アクセスできます。
どのような時に spmd を使用するか
SPMD における "single program (単一プログラム)" の側面は、同一のコードが複数のワーカーで実行されるということです。MATLAB® クライアントで 1 つのプログラムを実行すると、spmd ブロックというラベルが付いたそのプログラムの部分がワーカーで実行されます。spmd ブロックが完了すると、プログラムはクライアントでの実行を継続します。
"multiple data (複数のデータ)" の側面は、spmd ステートメントによりすべてのワーカーで同一コードが実行されても、そのコードに対しワーカーごとに固有の異なるデータを使用できるということです。このため、複数ワーカーによる複数データセットへの対応が可能になります。
spmd に適した典型的アプリケーションは、ワーカー間で通信または同期が必要な場合に複数のデータセットに対してプログラムの同時実行が必要なアプリケーションです。一般的なケースとして以下が挙げられます。
実行に長時間かかるプログラム —
spmdを使用すると、複数のワーカーで同時に解を計算できます。大規模なデータセットを扱うプログラム —
spmdによってデータを複数のワーカーに分散できます。
詳細については、spmd、parfor、および parfeval からの選択を参照してください。
spmd ステートメントの定義
spmd ステートメントの一般的な形式は、次のとおりです。
spmd <statements> end
メモ
並列プールが実行されていない場合、既定の並列設定では、spmd は既定のクラスター プロファイルを使用してプールを作成します。
<statements> で表されるコード ブロックは、並列プール内のすべてのワーカーで同時に並列実行されます。実行をこれらのワーカーの一部に制限する場合は、実行に使用するワーカーの正確な数を指定します。
spmd (n) <statements> end
このステートメントは、n 個のワーカーが spmd コードを実行するよう要求します。n は開いている並列プール内のワーカーの数以下でなければなりません。プールは十分に大きいものの n 個のワーカーは使用可能でない場合、ステートメントは十分な数のワーカーが使用可能になるまで待機します。n が 0 で、spmd ステートメントがワーカーを使用せずにクライアントでローカルに実行される場合は、現在実行中のプールがない場合と同じになります。
ワーカー数の範囲を指定できます。
spmd (m,n) <statements> end
この場合、spmd ステートメントには最低 m 個のワーカーが必要であり、最大で n 個のワーカーを使用します。
spmd ステートメントを実行するワーカー数の制御が重要な場合は、範囲を使用するのでなく、正確な数をクラスター プロファイルか spmd ステートメントで設定します。
たとえば、3 つのワーカー上に乱数行列を作成するには、以下を入力します。
spmd (3) R = rand(4,4); end
メモ
この章のこれ以降の例では、並列プールが既に開いており、spmd ステートメントのシーケンスの合間も開いたままであることを前提としています。
parfor ループと異なり、spmd ステートメントに使用されるワーカーにはそれぞれ固有の spmdIndex 値があります。このため、通常は固有のデータにアクセスする目的で、コードが特定のワーカーのみで実行されるように、あるいは実行をカスタマイズするように指定できます。
たとえば、spmdIndex に応じて異なるサイズの配列を作成します。
spmd (3) if spmdIndex==1 R = rand(9,9); else R = rand(4,4); end end
各ワーカーで spmdIndex に応じた固有のデータを読み込み、各ワーカーで同じ関数を使用してデータから結果を算出します。
spmd (3) labdata = load(['datafile_' num2str(spmdIndex) '.ascii']) result = MyFunction(labdata) end
spmd ステートメントを実行する複数のワーカーは同時に動作し、互いを認識しています。通信ジョブの場合と同様に、ワーカー間で直接に通信を制御し、データを転送し、対話型分散配列を使用することができます。
たとえば、spmd ステートメントで対話型分散配列を使用します。
spmd (3) RR = rand(30, codistributor()); end
各ワーカーには対話型分散配列 RR からの 30 行 10 列のセグメントがあります。対話型分散配列の詳細については、対話型分散配列の取り扱いを参照してください。
出力の表示
並列プールで spmd ステートメントを実行する場合、ワーカーからのコマンド ライン出力は、すべてクライアントのコマンド ウィンドウに表示されます。ワーカーは表示のない MATLAB セッションであるため、プールからはグラフィカルな出力 (Figure ウィンドウなど) は一切表示されません。
MATLAB パス
spmd ステートメントを実行するすべてのワーカーは、共通のコード ブロック内で呼び出されるすべての関数を実行できるよう、クライアントと同じ MATLAB 検索パスが設定されていなければなりません。このため、クライアントで cd、addpath または rmpath を使用する場合、そのコマンドは可能な範囲ですべてのワーカーでも実行されます。詳細については、parpool のリファレンス ページを参照してください。ワーカーをクライアントと異なるプラットフォームで実行する場合は、関数 pctRunOnAll を使用してすべてのワーカーに MATLAB パスを適切に設定してください。
エラー処理
spmd ステートメントの実行中にワーカーで発生したエラーは、クライアントにレポートされます。クライアントはすべてのワーカーでの実行の中断を試み、ユーザーにエラーをスローします。
ワーカーで生成されたエラーと警告にはワーカー ID (spmdIndex) が付けられ、MATLAB クライアントで受信された順にクライアントのコマンド ウィンドウに表示されます。
lastwarn が spmd 本体内で使用されている場合、その動作は spmd の最後では指定されません。
spmd の制限
入れ子関数
関数内の spmd ステートメントの本体から入れ子関数を参照することはできません。ただし、入れ子関数の関数ハンドルとして定義された変数を使用して入れ子関数を呼び出すことはできます。
spmd 本体はワーカーで実行されるため、spmd ステートメント内で呼び出される入れ子関数で変数が更新されても、その変数は外側の関数のワークスペースで更新されません。
入れ子にされた spmd ステートメント
spmd ステートメントの本体に別の spmd を直接含めることはできません。ただし、別の spmd ステートメントを含む関数を呼び出すことはできます。内側の spmd ステートメントは別の並列プールでは並列実行されず、そのステートメントが含まれる関数を実行するワーカーのシングル スレッドで逐次実行されます。
入れ子にされた parfor ループ
spmd ステートメントに parfor ループを含めることはできず、parfor ループの本体に spmd ステートメントを含めることはできません。
break、continue および return ステートメント
spmd ステートメントの本体に、break、continue または return の各ステートメントを含めることはできません。spmd の代わりとして parfeval または parfevalOnAll を検討してください。それらに対し cancel を使用できるためです。
グローバル変数および永続変数
spmd ステートメントの本体に、global または persistent の変数宣言を含めることはできません。その理由は、これらの変数がワーカー間で同期されないためです。関数内では global 変数または persistent 変数を使用できますが、それらの値はそれらの変数を作成するワーカーでのみ可視になります。global 変数の代わりに、関数の引数を使用して値を共有することをお勧めします。
無名関数
spmd ステートメントの本体で無名関数を定義することはできません。ただし、関数ハンドルを使用して無名関数を参照することはできます。
関数 inputname
spmd 内で inputname を使用して、引数番号に対応するワークスペース変数名を返すことはサポートされていません。その理由は、spmd ワーカーが MATLAB デスクトップのワークスペースにアクセスできないためです。これに対処するには、次の例に示すように、spmd の前に inputname を呼び出します。
a = 'a'; myFunction(a) function X = myFunction(a) name = inputname(1); spmd X.(name) = spmdIndex; end X = [X{:}]; end
関数 load
spmd ステートメント内で、出力構造体に代入しない load の構文はサポートされていません。spmd 内では、必ず load の出力を構造体に代入します。
関数 nargin または関数 nargout
spmd ステートメント内での次の使用はサポートされていません。
関数の引数を指定せずに
narginまたはnargoutを使用する現在実行している関数の呼び出し内にある入力引数または出力引数の数を検証する目的で
narginchkまたはnargoutchkを使用する
その理由は、ワーカーが MATLAB デスクトップのワークスペースにアクセスできないためです。これに対処するには、これらの関数を spmd の前に呼び出します。
myFunction('a','b') function myFunction(a,b) nin = nargin; spmd X = spmdIndex*nin; end end
P コード スクリプト
spmd ステートメント内から P コードのスクリプト ファイルを呼び出すことができますが、P コードのスクリプトに spmd ステートメントを含めることはできません。これに対処するには、P コード スクリプトの代わりに P コード関数を使用します。
変数 ans
spmd ステートメントの外部で定義された変数 ans を、spmd ステートメント内で参照することはサポートされていません。使用する前に、spmd ステートメントの本体内で変数 ans に代入しなければなりません。
データ コンテナー内の Composite と分散変数
Composite と分散配列は、spmd ステートメントの内部ではそれぞれ独自の最上位の変数として記述されていなければならず、構造体、cell 配列、オブジェクトなど、他のデータ コンテナーの中に隠れていてはなりません。
この例では、Composite オブジェクト C を cell 配列 Y に格納し、それを spmd ステートメントで入力引数として使用しています。その結果、MATLAB は実行時に警告を発行します。
spmd; C = 5; end Y = {C}; spmd disp(Y) end
同様に、dictionary などのオブジェクトに格納された Composite または分散配列も spmd ステートメントでは使用できません。この例では、dd は無効な分散配列であり、CC は無効な Composite です。
spmd; C = 5; d = ones(7,"codistributed"); end X = dictionary(["dist","comp"],{d,C}); spmd dd = X{"dist"} CC = X{"comp"} end
回避策として、spmd ステートメントの前に Composite または分散配列をデータ構造から抽出し、それを別の変数に代入します。
spmd; C = 5; d = ones(7,"codistributed"); end X = dictionary(["dist","comp"],{d,C}); dd = X{"dist"}; CC = X{"comp"}; spmd disp(dd) disp(CC) end
参考
spmd | parfor | parfeval | parfevalOnAll | distributed | Composite