Jak „skopiować” macierz bez tworzenia tymczasowej macierzy w pamięci, która spowodowała przepełnienie pamięci?

9

Przypisując macierz do znacznie większej przydzielonej pamięci, Matlab w jakiś sposób powieli ją podczas „kopiowania”, a jeśli matryca do skopiowania jest wystarczająco duża, nastąpi przepełnienie pamięci. To jest przykładowy kod:

main_mat=zeros(500,500,2000);
n=500;
slice_matrix=zeros(500,500,n);
for k=1:4
    parfor i=1:n
        slice_matrix(:,:,i)=gather(gpuArray(rand(500,500)));
    end
    main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)=slice_matrix; %This is where the memory will likely overflow
end

Każdy sposób, aby po prostu "rozbić zwanej dalej slice_matrixna main_matbez obciążania? Z góry dziękuję.

EDYTOWAĆ:

Przepełnienie nastąpiło, gdy main_matzostało przydzielone wcześniej. Jeśli main_matzostanie zainicjowany za pomocą main_mat=zeros(500,500,1);(mniejszy rozmiar), przepełnienie nie nastąpi, ale zostanie spowolnione, ponieważ alokacja nie zostanie wykonana przed przypisaniem do niej macierzy. Znacząco obniży to wydajność wraz ze kwzrostem zakresu .

Gregor Isack
źródło
1
Jeśli chodzi o pętle: zaleca się ustawienie pętli zewnętrznej na parforpętlę w celu optymalizacji . Dodatkowo parforskopiuj dane do każdego osobnego pracownika, zakładając, że 4 pracowników powiela dane cztery razy w pamięci RAM.
Adriaan
1
Co wskazuje na to, że Matlab kopiuje pamięć? Czy korzystasz z memoryfunkcji? Menedżer zadań? Błąd pamięci Matlaba? Przy jakiej linii kodu to się dzieje?
Eliahu Aaron,
Jak widać, gdzie skomentowałem kod, main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)występuje problem z przepełnieniem pamięci. Jest to weryfikowane, kiedy przydzieliłem main_matwcześniej, przepełni się, jeśli nie, nie będzie. Matlab zwróci błąd braku pamięci.
Gregor Isack,
Czy macierz 500x500x2000 mieści się w pamięci? To ~ 4 Gb. Zobacz stackoverflow.com/q/51987892/7328782, aby dowiedzieć się, dlaczego błąd braku pamięci mógł się zdarzyć tylko podczas zapisu do tablicy.
Cris Luengo,
Aby lepiej zrozumieć swój problem, czy możesz wstawić h=h+slice_matrix(end)przed main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)=slice_matrix;(i zainicjować h za pomocą 0)? Podejrzewam, że ta nowo dodana linia już spowoduje problemy z pamięcią.
Daniel

Odpowiedzi:

4

Głównym problemem jest to, że liczby zajmują więcej miejsca niż zera. main_mat=zeros(500,500,2000);zajmuje niewiele pamięci RAM, a main_mat = rand(500,500,2000);zajmuje dużo, bez względu na to, czy używasz GPU, czy parfor (w rzeczywistości, parfor sprawi, że będziesz używać więcej pamięci RAM). To nie jest nienaturalny obrzęk pamięci. Podążając za poniższym linkiem Daniela, wydaje się, że przypisanie zer tworzy tylko wskaźniki do pamięci, a pamięć fizyczna jest zapełniana tylko wtedy, gdy używasz macierzy dla „liczb”. Jest to zarządzane przez system operacyjny. Oczekuje się, że w systemach Windows, Mac i Linux, albo zrobisz to za pomocą Matlaba lub innych języków, takich jak C.

Yuval Harpaz
źródło
W tej chwili nie rozumiem już MATLAB. Po wpisaniu poleceń zeroscała pamięć wirtualna jest faktycznie przydzielana, ale pamięć nie jest używana. whospokazuje ten sam rozmiar dla obu macierzy, podczas gdy mój system operacyjny pokazuje inne zużycie pamięci. Usunąłem swój komentarz, ponieważ twoja odpowiedź zdecydowanie nie jest zła.
Daniel
3
Znalazłem coś, co to wyjaśnia: stackoverflow.com/questions/51987892/...
Daniel
Świetna odpowiedź! Dzięki.
JLev
@Gregor: Myślę, że to potwierdź, spróbuj oneszamiast tego zeros, dzięki temu pamięć zostanie faktycznie przydzielona w momencie wywołania odpowiedniej funkcji.
Daniel
Kiedy wszystko dobrze rozumiem, wniosek jest następujący: nie ma tymczasowej kopii. Wyjątki braku pamięci powstają, ponieważ main_matprzypisuje się im niezerowe wartości. Wcześniej przypisywano tylko pamięć wirtualną (przestrzeń adresową), teraz jest ona przypisywana do pamięci fizycznej.
Daniel
1

Usunięcie parforprawdopodobnie rozwiąże problem.

parfornie jest tam przydatny. MATLAB parfornie korzysta z równoległości pamięci współużytkowanej (tzn. Nie uruchamia nowych wątków), ale raczej z równoległości pamięci rozproszonej (uruchamia nowe procesy). Jest przeznaczony do rozdzielania pracy na zbiór lub węzły robocze. I chociaż działa również w obrębie jednego węzła (lub jednego komputera stacjonarnego), aby rozdzielić pracę na wiele rdzeni, nie jest to optymalny sposób wykonywania równoległości w obrębie jednego węzła.

Oznacza to, że każdy proces uruchamiany przez parformusi mieć własną kopię slice_matrix, co jest przyczyną dużej ilości pamięci używanej przez program.

Zobacz „Zdecyduj, kiedy używać parfor w dokumentacji MATLAB, aby dowiedzieć się więcej o tym parfori kiedy z niego korzystać.

Cris Luengo
źródło
1
Czy usuwanie parfor to jedyny sposób ? Przetwarzanie działa najlepiej, gdy zaprojektowałem go w ten sposób, ponieważ wszystko w nim parforwymaga procesora i procesora graficznego, co znacznie poprawiło wydajność.
Gregor Isack,
@GregorIsack: Poszedłem z twoim przykładowym kodem, nie wiedziałem, że faktycznie wykonałeś dużo pracy wewnątrz parfor. Jeśli tak, to tak, jest to prawdopodobnie przydatne. - Może jeśli slice_matrixnie jest gpuarray, nie zostanie skopiowane w zadaniu.
Cris Luengo,
Hmmm, nawet jeśli slice_matrixnie jest gpuArray, nadal mam objaw przepełnienia. Pozwolę otworzyć to pytanie, zobaczmy, czy jest jakieś alternatywne rozwiązanie. Dzięki za odpowiedź!
Gregor Isack,
0

Zakładam, że Twój kod to tylko przykładowy kod, który rand()reprezentuje niestandardowy kod w MVE. Jest więc kilka wskazówek i wskazówek dotyczących wykorzystania pamięci w Matlabie.

Fragment podręczników szkoleniowych MathWorks zawiera fragment:

Podczas przypisywania jednej zmiennej do drugiej w MATLAB, jak ma to miejsce przy przekazywaniu parametrów do funkcji, MATLAB w przejrzysty sposób tworzy odniesienie do tej zmiennej. MATLAB przerywa odwołanie i tworzy kopię tej zmiennej tylko wtedy, gdy kod modyfikuje jedną lub więcej wartości. To zachowanie, znane jako kopiowanie przy zapisie lub leniwe kopiowanie , odracza koszt kopiowania dużych zestawów danych, dopóki kod nie zmodyfikuje wartości. Dlatego jeśli kod nie wykonuje żadnych modyfikacji, nie ma potrzeby dodatkowego miejsca w pamięci i czasu wykonywania, aby skopiować zmienne.

Pierwszą rzeczą do zrobienia byłoby sprawdzenie wydajności (pamięci) kodu. Nawet kod doskonałych programistów można później zoptymalizować za pomocą (niewielkiej) mocy mózgu. Oto kilka wskazówek dotyczących wydajności pamięci

  • skorzystać z Nativ wektoryzacji Matlab, np sum(X,2), mean(X,2),std(X,[],2)
  • upewnij się, że Matlab nie musi rozszerzać macierzy ( niejawne rozszerzenie zostało ostatnio zmienione). Bardziej wydajne może być użyciebsxfun
  • używaj operacji na miejscu, np. x = 2*x+3zamiastx = 2*x+3
  • ...

Należy pamiętać, że optymalne wykorzystanie pamięci nie jest takie samo, jakbyś chciał skrócić czas obliczeń. Dlatego warto rozważyć zmniejszenie liczby pracowników lub powstrzymanie się od używania parforopcji -loop. (Ponieważ parfornie można używać pamięci współdzielonej, nie ma funkcji kopiowania przy zapisie przy użyciu Parallel Toolbox.

Jeśli chcesz bliżej przyjrzeć się swojej pamięci , co jest dostępne i które może być wykorzystane przez Matlab, sprawdź feature('memstats'). Co jest dla Ciebie interesujący jest pamięć wirtualna , która jest

Całkowita i dostępna pamięć związana z całym procesem MATLAB. Jest to ograniczone przez architekturę procesora i system operacyjny. lub użyj tego polecenia [user,sys] = memory.

Szybki węzeł boczny : Matlab konsekwentnie przechowuje macierze w pamięci. Musisz mieć duży blok wolnej pamięci RAM dla dużych matryc. Jest to również powód, dla którego chcesz alokować zmienne, ponieważ ich dynamiczne wymuszanie powoduje, że Matlab kopiuje całą macierz do większego miejsca w pamięci RAM za każdym razem, gdy przerasta bieżące miejsce.

Jeśli naprawdę masz problemy z pamięcią , możesz po prostu zagłębić się w sztukę typów danych - tak jak jest to wymagane w językach niższego poziomu. Np. Możesz zmniejszyć zużycie pamięci o połowę, używając pojedynczej precyzji bezpośrednio od początku main_mat=zeros(500,500,2000,'single');- btw, działa to również z rand(...,'single')i bardziej natywnymi funkcjami - chociaż kilka bardziej wyrafinowanych funkcji matlaba wymaga wprowadzenia typu double, co możesz znowu upcast.

max
źródło
0

Jeśli dobrze rozumiem, twoim głównym problemem jest to, że parfornie pozwala na współdzielenie pamięci. Pomyśl o każdym pracowniku jako prawie osobnej instancji Matlaba.

Zasadniczo istnieje tylko jedno obejście tego, o czym wiem (którego nigdy nie próbowałem), czyli „wspólna matryca” w Fileexchange: https://ch.mathworks.com/matlabcentral/fileexchange/28572-sharedmatrix

Więcej rozwiązań: jak sugerują inni: usuń parfor to z pewnością jedno rozwiązanie, zdobądź więcej pamięci RAM, użyj wysokich macierzy (które używają dysków twardych, gdy pamięć RAM jest pełna, przeczytaj tutaj ), dziel operacje na mniejsze części, na koniec, rozważ także alternatywę inną niż Matlab.

użytkownik2305193
źródło
0

Możesz użyć następującego kodu. W rzeczywistości nie potrzebujesz slice_matrix

main_mat=zeros(500,500,2000);
n=500;
slice_matrix=zeros(500,500,n);
for k=1:4
   parfor i=1:n
       main_mat(:,:,1+(k-1)*n + i - 1) = gather(gpuArray(rand(500,500)));
   end
   %% now you don't need this main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)=slice_matrix; %This is where the memory will likely overflow
end
mayank1513
źródło
Nie możesz tego zrobić w pętli parfor
Gregor Isack
Próbowałeś tego?
mayank1513
Jest powód, dla którego wyprowadziłem to z pętli parfoor. Nie wypróbowałem dokładnie tego samego kodu, ale wiedziałem, że nie zadziała z powodu indeksowania.
Gregor Isack