5

Jestem nowy w używaniu macierzy rozproszonych i kodowanych w Matlab. Kod równoległy, który wyprodukowałem, działa, ale jest znacznie wolniejszy niż wersja seryjna i nie mam pojęcia dlaczego. Poniższe przykłady kodu obliczają wartości własne hessańskich macierzy z danych wolumetrycznych.Powolne przetwarzanie równoległe Matlab z rozproszonymi tablicami

wersja seryjny:

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 

wersja równoległa:

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 

Jeśli ktoś może rzucić nieco światła na ten temat byłbym bardzo wdzięczny

+0

Jak długo trwa jedna iteracja? – Jonas

+0

otwierasz swój matlabpool? – Rasman

+0

@ Jonas Pojedyncza iteracja (nad zmienną i) w wersji szeregowej zajmuje około 1,7 sekundy.Pojedyncza iteracja w wersji równoległej nie kończy się w ciągu 5 minut, w którym to momencie zakończyłem wykonanie. – Hampycalc

Odpowiedz

2

Oto ponownie napisana wersja Twojego kodu. Dzieliłem pracę nad zewnętrzną pętlą, a nie jak w twoim przypadku - najbardziej wewnętrzną pętlą. Wyraźnie przydzieliłem także lokalne części wektora wyników d oraz lokalną część macierzy Hesji.

W swoim kodzie polegasz na drange, aby dzielić pracę, i uzyskujesz dostęp do rozproszonych tablic bezpośrednio, aby uniknąć wyodrębniania części lokalnej. Wprawdzie nie powinno to spowodować tak wielkiego spowolnienia, jeśli MATLAB wykona wszystko poprawnie. Najważniejsze jest to, że nie wiem, dlaczego twój kod jest tak powolny - najprawdopodobniej dlatego, że MATLAB wykonuje niektóre zdalne dane, mimo że rozpowszechniłeś swoje macierze.

W każdym razie poniższy kod działa i daje całkiem dobre przyspieszenie na moim komputerze przy użyciu 4 laboratoriów. Wygenerowałem syntetyczne losowe dane wejściowe, aby mieć coś do pracy. Spójrz na komentarze. Jeśli coś jest niejasne, mogę je później rozwinąć.

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) 

a wyjście

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 

Edit Sprawdziłem działanie na przekształconego matrycy DHess=[3x3xN]. Wydajność nie jest dużo lepsza (10%), więc nie jest znaczna. Ale może możesz wdrożyć eig trochę inaczej? W końcu są to macierze, z którymi macie do czynienia.

+0

To wspaniale, że poświęciliście trochę czasu na przedstawienie tego przykładu, ponieważ będę mógł wykorzystać te pomysły w wielu przyszłych projektach. Kilka pytań: użyłem 'drange' w moim kodzie, jaki jest cel tego, jeśli musisz użyć 'getLocalPart'. Używając Twojego kodu, otrzymuję komunikat o błędzie: "Błąd przy użyciu distcompserialize Błąd podczas serializacji" na linii DHess_dist = codistributed (DHess, codistributor1d (3)); Wielkość mojego wejścia D wynosi około 512 x 512 x 200, być może rozmiar jest problemem; chociaż wydaje się, że nie powinno mieć znaczenia, jak duża jest tablica, ponieważ jest to główny cel przetwarzania równoległego. – Hampycalc

+0

Powinienem również stwierdzić, że D jest podwójne. – Hampycalc

+0

@Hampycalc Przepraszam, najwyraźniej brakowało mi tego, że używałeś 'drange'. Będę edytować moją odpowiedź - mimo wszystko podzieliłeś swoją pracę. Mój błąd. – angainor

1

Ty nie określił, gdzie” ve otworzył twój matlabpool, a to będzie główny czynnik decydujący o tym, jaki masz przyspieszenie.

Jeśli korzystasz z "lokalnego" programu planującego, często nie ma korzyści z korzystania z rozproszonych tablic. W szczególności, jeśli czasochłonne operacje są już wielowątkowe w środowisku MATLAB, to prawie na pewno spowolnią działanie podczas używania lokalnego programu planującego, ponieważ pracownicy programu Matlabpool działają w trybie jednowątkowym.

Jeśli korzystasz z innego programu planującego z pracownikami na oddzielnej maszynie, możesz uzyskać przyspieszenie, ale to zależy od tego, co robisz. Oto przykład tutaj: http://www.mathworks.com/products/parallel-computing/examples.html?file=/products/demos/shipping/distcomp/paralleldemo_backslash_bench.html, który pokazuje niektóre testy porównawcze operatora MATLAB: \.

Wreszcie, warto zauważyć, że indeksowanie rozproszonych tablic jest niestety dość powolne, szczególnie w porównaniu z wbudowanym indeksowaniem MATLAB. Jeśli możesz wyodrębnić "lokalną część" swoich kodowanych tablic wewnątrz bloku SPMD i pracować wyłącznie z tymi, co może również pomóc.

+0

Dziękuję za odpowiedź. Zapomniałem wspomnieć, że otwieram matlabpool przy użyciu "lokalnego" profilu z 6 laboratoriami, jednak pojedyncza iteracja trwa około 1,7 sekundy przy użyciu wersji szeregowej, a wersja równoległa nie wykonuje jednej iteracji w ciągu 5 minut. – Hampycalc

+0

Sam operacja (eig) nie jest wielowątkowa i nie musi być tak jak macierz, jako że wejście to tylko 3x3. Przydałoby się to jednak poprzez możliwość przypisania każdej zewnętrznej lub wewnętrznej pętli do osobnego rdzenia. Spróbuję zasugerować twój pomysł o wyodrębnieniu "części lokalnej" i złożyć raport. – Hampycalc

Powiązane problemy