ドキュメンテーション センター

  • 評価版
  • 製品アップデート

最新のリリースでは、このページがまだ翻訳されていません。 このページの最新版は英語でご覧になれます。

プログラミング上の考慮事項

MATLAB パス

parfor ループを実行するすべてのワーカーは、ループの本体内で呼び出されるすべての関数を実行できるよう、クライアントと同じ MATLAB® 検索パスが設定されていなければなりません。このため、クライアントで cdaddpath または rmpath を使用する場合、そのコマンドは可能な範囲ですべてのワーカーでも実行されます。詳細は、parpool のリファレンス ページを参照してください。ワーカーをクライアントと異なるプラットフォームで実行する場合は、関数 pctRunOnAll を使用してすべてのワーカーに MATLAB 検索パスを適切に設定してください。

parfor ループを含む関数ファイルは、parfor を実行するプールのワーカーの検索パスに存在しているか、または並列プールの AttachedFiles または AdditionalPaths 設定によってワーカーで使用できるようにしておかなければなりません。

エラー処理

parfor ループの実行中にエラーが発生した場合、処理中のすべての反復が終了し、新しい反復は開始されず、ループは終了します。

ワーカーで発生したエラーと警告は、クライアントの MATLAB で受信された順にクライアントのコマンド ウィンドウにワーカー ID 付きで表示されます。

lastwarn がループ本体内で使用されている場合、その動作は parfor の最後では指定されません。

制限

明確な変数名

parfor ループ内の変数として MATLAB で明確に識別できない名前を使用すると、関数を参照しているものと解析時に想定されます。その上で、実行に際して関数が見つからないと、MATLAB でエラーが発生します (MATLAB ドキュメンテーションの「変数名」を参照)。たとえば、次のコードでは、f(5)f という配列の 5 番目の要素を参照しているか、または引数 5 をもつ f という関数を参照しているかのいずれかです。f がコード内で変数として明確に定義されていない場合、MATLAB ではコード実行時にパスで関数 f を検索します。

parfor i=1:n
   ...
   a = f(5);
   ...
end

透明度

parfor ループの本体は "透明" でなければなりません。つまり、変数への参照はすべて "可視である" (すなわち、プログラムのソース コードに記述されている) 必要があります。

次の例で、Xparfor の本体において入力変数として可視でない (文字列 'X' のみが eval に渡されている) ため、ワーカーには転送されません。その結果、MATLAB は実行時にエラーを発します。

X = 5;
parfor ii = 1:4
    eval('X');
end

同様に、parfor ステートメント内で clear を実行して、ワーカーのワークスペースから変数を消去することはできません。

parfor ii= 1:4
    <statements...>
    clear('X')  % cannot clear: transparency violation
    <statements...>
end

回避策として、parfor ステートメント内で変数が不要になったときにその値を空にすることで、変数によって使用されていたメモリの大部分を解放できます。

parfor ii= 1:4
    <statements...>
    X = [];
    <statements...>
end

透明性を損なう他の関数の例は、workspace 引数が 'caller' として指定されている evalcevalin および assigninload の出力が変数に代入されない場合の save および load です。parfor ループ内からスクリプトを実行すると、スクリプトが親ワークスペースの変数にアクセス (読み取りまたは書き込み) する際に透明性が損なわれる可能性があります。この問題を回避するには、スクリプトを関数に変換し、必要な変数を入力引数または出力引数として指定してその関数を呼び出します。

MATLAB は、parfor 本体から呼び出される関数に含まれる eval および evalc ステートメントを正常に "実行します"。

関数ハンドルを参照するスライス化された変数

スライス化された入力変数は特定の方法でセグメント化され、並列プールのワーカーに分散されるため、スライス化された入力変数を使用して関数ハンドルを参照することはできません。parfor のインデックス変数を引数として関数ハンドルを呼び出す必要がある場合は、feval を使用します。

たとえば、次を実行する for ループがあるとします。

B = @sin;
for ii = 1:100
    A(ii) = B(ii);
end

これに対応する parfor ループでは、B が関数ハンドルを参照できません。このため、この問題には feval を用いて対処します。

B = @sin;
parfor ii = 1:100
    A(ii) = feval(B, ii);
end

分散不能な関数

parfor ループや parfor ループで呼び出される任意の関数において、厳密には計算的特性をもたない関数 (たとえば、inputplotkeyboard など) を使用する場合、その関数はワーカー上で動作します。その結果、ワーカーのプロセスがハングアップしたり視覚的効果がなくなる場合があります。

入れ子関数

parfor ループの本体は「入れ子関数」を参照できません。ただし、関数ハンドルを使用して入れ子関数を呼び出すことはできます。

入れ子にされたループ

parfor ループの本体には、別の parfor ループを含めることができません。ただし、別の parfor ループを含む関数を呼び出すことはできます。

しかし、ワーカーは並列プールを開くことができないため、入れ子にされた内側の parfor ループを並列実行することはできません。つまり、入れ子にされた parfor ループのうち、1 つのレベルのみが並列実行できます。外側のループが並列プールで並列実行される場合、内側のループは各ワーカーで逐次実行されます。外側のループがクライアントで逐次実行される (たとえば、parfor でワーカーが指定されない場合など) 場合、内側のループを含む関数は、プールのワーカーでその内側のループを並列実行できます。

parfor ループの本体には、for ループを含めることができます。スライス化された配列のインデックス付けに内側のループの変数を使用することはできますが、それは変数を式の一部でなくシンプルな形式で使用する場合に限ります。たとえば、以下のようにします。

A = zeros(4,5);
parfor j = 1:4
    for k = 1:5
        A(j,k) = j + k;
    end
end
A

parforfor ループをさらに入れ子にすることも可能です。

入れ子にされた for ループに関する制限-  変数が適切に分類されるよう、parfor に入れ子にされる for ループの範囲は定数または変数で定義しておかなければなりません。以下の例では、左側のコードは機能しません。for ループの上限が関数呼び出しで定義されているためです。右側のコードでは、最初に parfor の外側でブロードキャスト変数または定数変数を定義することで、この問題を回避しています。

A = zeros(100, 200);
parfor i = 1:size(A, 1)
   for j = 1:size(A, 2)
      A(i, j) = plus(i, j);
   end
end
A = zeros(100, 200);
n = size(A, 2);
parfor i = 1:size(A,1)
   for j = 1:n
      A(i, j) = plus(i, j);
   end
end

スライス化された配列のインデックス付けに入れ子にされた for ループの変数を使用する場合は、変数を式の一部でなくシンプルな形式で使用しなければなりません。たとえば、次の左側のコードは機能しませんが、右側のコードは機能します。

A = zeros(4, 11);
parfor i = 1:4
   for j = 1:10
      A(i, j + 1) = i + j;
   end
end
A = zeros(4, 11);
parfor i = 1:4
   for j = 2:11
      A(i, j) = i + j + 1;
   end
end

入れ子にされた for ループを使用して、スライス化された配列にインデックスを付ける場合、parfor ループ内の他の場所でその配列を使用することはできません。たとえば、次の例で左側のコードは機能しません。A がスライス化され、入れ子にされた for ループ内でインデックス付けされているためです。これに対し、右側のコードは機能します。入れ子にされたループの外側で vA に代入されているためです。

A = zeros(4, 10);
parfor i = 1:4
    for j = 1:10
        A(i, j) = i + j;
    end
    disp(A(i, 1))
end
A = zeros(4, 10);
parfor i = 1:4
    v = zeros(1, 10);
    for j = 1:10
        v(j) = i + j;
    end
    disp(v(1))
    A(i, :) = v;
end

parfor 内で (相互に入れ子にされていない) 複数の for ループを使用し、スライス化された単一の配列にインデックスを付ける場合、これらのループは同じ値範囲で実行されなければなりません。次の例では、左側のコードは機能しません。j および k が異なる値範囲でループに使用されているためです。右側のコードは機能し、スライス化された配列 A の異なった部分にインデックスを付けます。

A = zeros(4, 10);
parfor i = 1:4
   for j = 1:5
      A(i, j) = i + j;
   end
   for k = 6:10
      A(i, k) = pi;
   end
end
A = zeros(4, 10);
parfor i = 1:4
   for j = 1:10
      if j < 6
         A(i, j) = i + j;
      else
         A(i, j) = pi;
      end
   end
end

入れ子にされた spmd ステートメント

parfor ループの本体に spmd ステートメントを含めることはできず、spmd ステートメントに parfor ループを含めることはできません。

break および return ステートメント

parfor ループの本体には、break または return ステートメントを含めることができません。

グローバル変数および永続変数

parfor ループの本体には、global または persistent の変数宣言を含めることができません。

ハンドル クラス

ループ反復中にワーカーでハンドル クラスに加えられた変更は、クライアントに自動伝播されません。

P コード スクリプト

parfor ループ内から P コード スクリプト ファイルを呼び出すことができますが、P コード スクリプトに parfor ループを含めることはできません。

parfor ループの構造体配列

一時変数としての構造体の作成-  ドット表記の割り当てを使用して parfor ループで構造体を作成することはできません。たとえば、次のコードではループ内の両方の行で分類エラーが発生します。

parfor i = 1:4
   temp.myfield1 = rand();
   temp.myfield2 = i;
end 

この問題を回避するには、関数 struct を使用してループ内で構造体を作成するか、少なくとも最初のフィールドを作成します。以下のコードはこの 2 通りの解決方法を示しています。

parfor i = 1:4
    temp = struct();
    temp.myfield1 = rand();
    temp.myfield2 = i;
end 
parfor i = 1:4
    temp = struct('myfield1',rand(),'myfield2',i);
end

構造体フィールドのスライス化-  parfor ループで構造体フィールドをスライス化された "入力または出力" 配列として使用することはできません。つまり、構造体フィールドの要素のインデックス付けにループ変数を使用することはできません。たとえば、次のコードではインデックス付けが原因でループ内の両方の行で分類エラーが発生します。

parfor i = 1:4
    outputData.outArray1(i) = 1/i;
    outputData.outArray2(i) = i^2;
end 

スライス化された出力の問題を回避するには、以下のコードのようにループ内でスライス化された別の配列を使用し、ループが完了した後で構造体フィールドを割り当てます。

parfor i = 1:4
    outArray1(i) = 1/i;
    outArray2(i) = i^2;
end
outputData = struct('outArray1',outArray1,'outArray2',outArray2); 

スライス化された入力の問題を回避するには、ループの前に構造体フィールドを別の配列に割り当て、この新しい配列をスライス化された入力に対して使用します。

inArray1 = inputData.inArray1;
inArray2 = inputData.inArray2;
parfor i = 1:4
    temp1 = inArray1(i);
    temp2 = inArray2(i);
end

parfor ループでのオブジェクトの使用

parfor ループとオブジェクトの受け渡しを行う場合、オブジェクトは保存と読み込みに適したものでなければなりません。詳細は、「保存と読み込みの処理について」を参照してください。

パフォーマンスについて

配列のスライス化

変数を parfor ループの前に初期化して parfor ループ内で使用する場合、その変数は、ループ反復を評価する各 MATLAB ワーカーに渡さなければなりません。ループ内で使用される変数だけがクライアント ワークスペースから渡されます。ただし、変数のすべての出現箇所にループ変数でインデックスを付ける場合、各ワーカーは配列の必要部分のみを受け取ります。詳細は、「配列を作成する場所」を参照してください。

ローカル ワーカーとクラスター ワーカー

ローカル ワーカーでコードを実行すると、クラスターのリソースを使用せずにアプリケーションをテストするという便宜が得られる場合があります。しかし、ローカル ワーカーの使用には欠点や制限もあります。データ転送がネットワーク経由で行われないため、ローカル ワーカーでの転送動作ではネットワークでの通常の動作が示されません。詳細は、「ローカル ワーカーでの最適化とクラスター ワーカーでの最適化」を参照してください。

MATLAB ソフトウェアの旧バージョンとの互換性

7.5 (R2007b) より前のバージョンの MATLAB では、キーワード parfor で指定されるスタイルは、MATLAB 7.5 以降で使用可能な parfor ループのスタイルよりも限られていました。この古いスタイルでは (spmd ステートメントや並列ジョブなどの内側で) 対話型分散配列との使用が想定されていましたが、drange を使用して範囲を定義する for ループに置き換えられました。「分散範囲に対するループ (for-drange)」を参照してください。

parfor キーワードの以前および現在の機能を以下の表にまとめます。

機能MATLAB 7.5 より前の構文現在の構文
対話型分散配列用の並列ループ
parfor i = range
  loop body
    .
    .
end
for i = drange(range)
  loop body
    .
    .
end
負荷を暗黙的に分散する並列ループ実装されていません
parfor i = range
  loop body
    .
    .
end

この情報は役に立ちましたか?