5

私はmatlabで分散型および共分散型アレイを使用するのが初めてです。私が作った並列コードは動作しますが、シリアルバージョンよりもはるかに遅く、なぜ私にはわかりません。以下のコード例は、ボリュームデータからヘッセ行列の固有値を計算します。分散アレイによるMatlabの遅い並列処理

シリアルバージョン:

S = size(D); 
Dsmt=imgaussian(D,2,20); 
[fx, fy, fz] = gradient(Dsmt); 
DHess = zeros([3 3 S(1) S(2) S(3)]); 
[DHess(1,1,:,:,:), DHess(1,2,:,:,:), DHess(1,3,:,:,:)] = gradient(fx); 
[DHess(2,1,:,:,:), DHess(2,2,:,:,:), DHess(2,3,:,:,:)] = gradient(fy); 
[DHess(3,1,:,:,:), DHess(3,2,:,:,:), DHess(3,3,:,:,:)] = gradient(fz); 

d = zeros([3 S(1) S(2) S(3)]); 
for i = 1 : S(1) 
    fprintf('Slice %d out of %d\n', i, S(1)); 
    for ii = 1 : S(2) 
     for iii = 1 : S(3) 
      d(:,i,ii,iii) = eig(squeeze(DHess(:,:,i,ii,iii))); 
     end 
    end 
end 

パラレルバージョンは:誰かが私は非常に感謝される問題に関するいくつかの光を当てることができれば

S = size(D); 
Dsmt=imgaussian(D,2,20); 
[fx, fy, fz] = gradient(Dsmt); 
DHess = zeros([3 3 S(1) S(2) S(3)]); 
[DHess(1,1,:,:,:), DHess(1,2,:,:,:), DHess(1,3,:,:,:)] = gradient(fx); 
[DHess(2,1,:,:,:), DHess(2,2,:,:,:), DHess(2,3,:,:,:)] = gradient(fy); 
[DHess(3,1,:,:,:), DHess(3,2,:,:,:), DHess(3,3,:,:,:)] = gradient(fz); 
CDHess = distributed(DHess); 
spmd 
    d = zeros([3 S(1) S(2) S(3)], codistributor('1d',4)); 
    for i = 1 : S(1) 
     fprintf('Slice %d out of %d\n', i, S(1)); 
     for ii = 1 : S(2) 
      for iii = drange(1 : S(3)) 
       d(:,i,ii,iii) = eig(squeeze(CDHess(:,:,i,ii,iii))); 
      end 
     end 
    end 
end 

+0

1回の反復ではどのくらいの時間がかかりますか? – Jonas

+0

あなたはあなたのmatlabpoolを開いていますか? – Rasman

+0

@Jonasシリアルバージョンの(反復変数iの)1回の反復には約1.7秒かかります。パラレルバージョンでの1回の繰り返しは、5分以上で完了せず、その時点で実行を終了しました。 – Hampycalc

答えて

2

ここにコードの書き直し版があります。私はあなたの場合と同じではなく、一番外側のループ、つまり一番内側のループを分割しました。私はまた、d結果ベクトルのローカル部分とヘッセ行列のローカル部分を明示的に割り当てました。

コードでは、drangeを使用して作業を分割し、分散アレイに直接アクセスしてローカル部分を抽出しないようにします。確かに、MATLABがすべてを正しく行った場合、このような大きな減速をもたらすべきではありません。要するに、あなたのコードがなぜそんなに遅いのか分かりません。ほとんどの場合、MATLABは行列を配布したにもかかわらず、いくつかのリモートデータにアクセスするためです。

とにかく、以下のコードが実行され、4つのラボを使用してコンピュータでかなり高速化されます。私は、何かを行うために合成ランダム入力データを生成しました。コメントを見てください。何か不明な点がある場合は、後で詳しく説明します。

clear all; 

D = rand(512, 512, 3); 
S = size(D); 
[fx, fy, fz] = gradient(D); 

% this part could also be parallelized - at least a bit. 
tic; 
DHess = zeros([3 3 S(1) S(2) S(3)]); 
[DHess(1,1,:,:,:), DHess(1,2,:,:,:), DHess(1,3,:,:,:)] = gradient(fx); 
[DHess(2,1,:,:,:), DHess(2,2,:,:,:), DHess(2,3,:,:,:)] = gradient(fy); 
[DHess(3,1,:,:,:), DHess(3,2,:,:,:), DHess(3,3,:,:,:)] = gradient(fz); 
toc 

% your sequential implementation 
d = zeros([3, S(1) S(2) S(3)]); 
disp('sequential') 
tic 
for i = 1 : S(1) 
    for ii = 1 : S(2) 
     for iii = 1 : S(3) 
      d(:,i,ii,iii) = eig(squeeze(DHess(:,:,i,ii,iii))); 
     end 
    end 
end 
toc 

% my parallel implementation 
disp('parallel') 
tic 
spmd 
    % just for information 
    disp(['lab ' num2str(labindex)]); 

    % distribute the input data along the third dimension 
    % This is the dimension of the outer-most loop, hence this is where we 
    % want to parallelize! 
    DHess_dist = codistributed(DHess, codistributor1d(3)); 
    DHess_local = getLocalPart(DHess_dist); 

    % create an output data distribution - 
    % note that this time we split along the second dimension 
    codist = codistributor1d(2, codistributor1d.unsetPartition, [3, S(1) S(2) S(3)]); 
    localSize = [3 codist.Partition(labindex) S(2) S(3)]; 

    % allocate local part of the output array d 
    d_local = zeros(localSize); 

    % your ordinary loop, BUT! the outermost loop is split amongst the 
    % threads explicitly, using local indexing. In the loop only local parts 
    % of matrix d and DHess are accessed 
    for i = 1:size(d_local,2) 
     for ii = 1 : S(2) 
      for iii = 1 : S(3) 
       d_local(:,i,ii,iii) = eig(squeeze(DHess_local(:,:,i,ii,iii))); 
      end 
     end 
    end 

    % assemble local results to a codistributed matrix 
    d_dist = codistributed.build(d_local, codist); 
end 
toc 

isequal(d, d_dist) 

と出力

Elapsed time is 0.364255 seconds. 
sequential 
Elapsed time is 33.498985 seconds. 
parallel 
Lab 1: 
    lab 1 
Lab 2: 
    lab 2 
Lab 3: 
    lab 3 
Lab 4: 
    lab 4 
Elapsed time is 9.445856 seconds. 

ans = 

    1 

編集私が作り直さ行列DHess=[3x3xN]のパフォーマンスをチェックしています。パフォーマンスはあまり良くありません(10%)ので、それほど重要ではありません。しかし、多分eigを多少実装することはできますか?結局のところ、それらはあなたが扱っている3x3行列です。

+0

多くの将来のプロジェクトでこれらのアイデアを使用できるようになるため、この例を提供する時間を取ったことは素晴らしいことです。 2つの質問:私はコードで 'drange'を使用しました。なぜなら、あなたは 'getLocalPart'を使用しなければならない場合、これの目的は何ですか。あなたのコードを使用して私はエラーが発生しています: 'diffcompserializeを使用しているエラー 行のシリアル化中にエラー' DHess_dist = codistributed(DHess、codistributor1d(3));私の入力Dのサイズは約512x512x200ですが、おそらくサイズは問題です。並列処理の主目的であるので、配列の大きさは関係ありません。 – Hampycalc

+0

また、Dがdoubleであることを示す必要があります。 – Hampycalc

+0

@Hampycalc申し訳ありませんが、私は明らかにあなたが 'drange'を使ったという事実を逃しました。私は私の答えを編集します - あなたは結局あなたの仕事を分けました。私の悪い。 – angainor

1

あなたは「あなたの場所に指定されていませんでしたあなたのmatlabpoolを開きました。それがあなたのスピードアップを決定する主な要因になります。

'ローカル'スケジューラを使用している場合、分散アレイを使用する利点はないことがよくあります。特に、時間のかかる操作がすでにMATLABでマルチスレッド化されている場合は、matlabpoolワーカーがシングルスレッドモードで動作するため、ローカルスケジューラを使用するとほとんど確実に速度が低下します。

別のマシンのワーカーで他のスケジューラを使用している場合は、スピードアップを得ることができるかもしれませんが、それはあなたが何をしているかによって異なります。ここにはhttp://www.mathworks.com/products/parallel-computing/examples.html?file=/products/demos/shipping/distcomp/paralleldemo_backslash_bench.htmlという例があり、MATLABの\演算子のベンチマークを示しています。

最後に、分散アレイのインデックス作成は、残念なことに、特にMATLABの組み込みインデックス作成と比較して遅いことに注意してください。あなたがSPDのブロックの中にあなたの共用された配列の 'ローカル部分'を抽出し、それをもって排他的に働くことができれば、それは役に立ちます。

+0

返信いただきありがとうございます。私は、6つのラボで 'ローカル'プロファイルを使用してmatlabpoolを開いていることを忘れていましたが、1回の反復はシリアルバージョンを使用すると約1.7秒で、パラレルバージョンは5分間で1回の反復を完了しません。 – Hampycalc

+0

操作(eig)自体はマルチスレッド化されておらず、入力として取られた行列は3x3である必要はありません。しかし、それはそれぞれの外側または内側のループを別個のコアに割り当てることができるという利点があります。 「ローカル部分」を抽出して報告することについて提案された考えを試みます。 – Hampycalc

関連する問題