Robię pewne testy porównawcze z CUDA, C ++, C #, Java i używam MATLAB do weryfikacji i generowania macierzy. Kiedy wykonuję mnożenie macierzy za pomocą MATLAB, 2048x2048
nawet większe macierze są prawie natychmiast mnożone.
1024x1024 2048x2048 4096x4096
--------- --------- ---------
CUDA C (ms) 43.11 391.05 3407.99
C++ (ms) 6137.10 64369.29 551390.93
C# (ms) 10509.00 300684.00 2527250.00
Java (ms) 9149.90 92562.28 838357.94
MATLAB (ms) 75.01 423.10 3133.90
Tylko CUDA jest konkurencyjna, ale myślałem, że przynajmniej C ++ będzie nieco zbliżony i nie 60 razy wolniejszy. Nie wiem też, co myśleć o wynikach w C #. Algorytm jest tak samo jak C ++ i Java, ale nie olbrzymi skok 2048
z 1024
.
Jak MATLAB wykonuje tak szybkie mnożenie macierzy?
Kod C ++:
float temp = 0;
timer.start();
for(int j = 0; j < rozmer; j++)
{
for (int k = 0; k < rozmer; k++)
{
temp = 0;
for (int m = 0; m < rozmer; m++)
{
temp = temp + matice1[j][m] * matice2[m][k];
}
matice3[j][k] = temp;
}
}
timer.stop();
Odpowiedzi:
Oto moje wyniki z użyciem MATLAB R2011a + Parallel Computing Toolbox na maszynie z Tesla C2070:
MATLAB wykorzystuje wysoce zoptymalizowane biblioteki do mnożenia macierzy, dlatego zwykłe mnożenie macierzy MATLAB jest tak szybkie.
gpuArray
Wersja używa magmy .Zaktualizuj za pomocą R2014a na maszynie z Tesla K20c, a nowe
timeit
igputimeit
funkcje:Zaktualizuj za pomocą R2018b na maszynie WIN64 z 16 fizycznymi rdzeniami i Tesla V100:
(Uwaga: w pewnym momencie (zapomnę, kiedy dokładnie)
gpuArray
zmieniłem z MAGMA na cuBLAS - MAGMA jest nadal używana do niektórychgpuArray
operacji)źródło
Tego rodzaju pytania powracają i należy na nie odpowiedzieć jaśniej niż „MATLAB używa wysoce zoptymalizowanych bibliotek” lub „MATLAB używa MKL” raz na przepełnieniu stosu.
Historia:
Mnożenie macierzy (wraz z wektorem macierzy, mnożeniem wektora i wieloma rozkładami macierzy) jest (są) najważniejszymi problemami w algebrze liniowej. Inżynierowie rozwiązują te problemy z komputerami od pierwszych dni.
Nie jestem ekspertem od historii, ale najwyraźniej wtedy wszyscy przepisali jego wersję FORTRAN za pomocą prostych pętli. Potem pojawiła się pewna standaryzacja, wraz z identyfikacją „jąder” (podstawowych procedur), które większość problemów algebry liniowej potrzebowała do rozwiązania. Te podstawowe operacje zostały następnie znormalizowane w specyfikacji o nazwie: podprogramy podstawowej algebry liniowej (BLAS). Inżynierowie mogliby wówczas nazwać te standardowe, dobrze przetestowane procedury BLAS w swoim kodzie, co znacznie ułatwi ich pracę.
BLAS:
BLAS ewoluował z poziomu 1 (pierwsza wersja, która definiowała operacje na wektorach skalarnych i wektorach) do poziomu 2 (operacje na macierzach wektorowych) do poziomu 3 (operacje na macierzach) i dostarczał coraz więcej „jąder”, więc bardziej znormalizowanych i więcej podstawowych operacji algebry liniowej. Oryginalne implementacje FORTRAN 77 są nadal dostępne na stronie internetowej Netlib .
W kierunku lepszej wydajności:
Tak więc z biegiem lat (zwłaszcza między wydaniami BLAS poziomu 1 i poziomu 2: wczesne lata 80.) zmienił się sprzęt wraz z nadejściem operacji wektorowych i hierarchii pamięci podręcznej. Te zmiany umożliwiły znaczne zwiększenie wydajności podprogramów BLAS. Następnie pojawili się różni dostawcy, którzy wdrażali procedury BLAS, które były coraz bardziej wydajne.
Nie znam wszystkich historycznych wdrożeń (wtedy nie urodziłem się ani nie byłem dzieckiem), ale na początku 2000 roku pojawiły się dwa najbardziej znaczące: Intel MKL i GotoBLAS. Twój Matlab korzysta z Intel MKL, który jest bardzo dobrym, zoptymalizowanym BLAS, a to wyjaśnia doskonałą wydajność, którą widzisz.
Szczegóły techniczne dotyczące mnożenia macierzy:
Dlaczego więc Matlab (MKL) jest tak szybki w
dgemm
(dwukrotne ogólne mnożenie macierzy-macierzy)? Mówiąc prosto: ponieważ wykorzystuje wektoryzację i dobre buforowanie danych. W bardziej złożonych terminach: patrz artykuł dostarczony przez Jonathana Moore'a.Zasadniczo, gdy wykonujesz mnożenie w dostarczonym kodzie C ++, wcale nie jesteś przyjazny dla pamięci podręcznej. Ponieważ podejrzewam, że stworzyłeś tablicę wskaźników do tablic wierszowych, twoje dostępy w wewnętrznej pętli do k-tej kolumny „matice2”:
matice2[m][k]
są bardzo wolne. Rzeczywiście, kiedy uzyskujesz dostępmatice2[0][k]
, musisz uzyskać k-ty element tablicy 0 macierzy. Następnie w następnej iteracji musisz uzyskać dostępmatice2[1][k]
, który jest k-tym elementem innej tablicy (tablica 1). Następnie w następnej iteracji uzyskujesz dostęp do kolejnej tablicy i tak dalej ... Ponieważ cała matrycamatice2
nie mieści się w najwyższych pamięciach podręcznych (ma8*1024*1024
duże bajty), program musi pobrać żądany element z pamięci głównej, tracąc dużo czas.Jeśli właśnie przetransponowałeś macierz, aby dostęp był w ciągłych adresach pamięci, twój kod działałby już znacznie szybciej, ponieważ teraz kompilator może ładować całe wiersze w pamięci podręcznej w tym samym czasie. Po prostu wypróbuj zmodyfikowaną wersję:
Możesz więc zobaczyć, jak po prostu pamięć podręczna znacznie zwiększyła wydajność kodu. Teraz prawdziwe
dgemm
implementacje wykorzystują to na bardzo szerokim poziomie: wykonują mnożenie na blokach macierzy określonych przez rozmiar TLB (bufor lookaside tłumaczenia, krótka historia: co można skutecznie buforować), aby przesyłać strumieniowo do procesora dokładnie ilość danych, które może przetworzyć. Innym aspektem jest wektoryzacja, używają one wektoryzowanych instrukcji procesora w celu uzyskania optymalnej przepustowości instrukcji, czego tak naprawdę nie można zrobić z wieloplatformowego kodu C ++.Wreszcie, ludzie twierdzący, że jest to spowodowane algorytmem Strassena lub Coppersmitha-Winograda, są w błędzie, oba te algorytmy nie są możliwe do wdrożenia w praktyce ze względu na wspomniane wyżej względy sprzętowe.
źródło
To dlaczego . MATLAB nie wykonuje naiwnego mnożenia macierzy przez zapętlanie każdego elementu, tak jak w kodzie C ++.
Oczywiście zakładam, że właśnie użyłeś
C=A*B
zamiast pisać funkcję mnożenia.źródło
Matlab włączył LAPACK jakiś czas temu, więc zakładam, że ich mnożenie macierzy używa czegoś co najmniej tak szybko. Kod źródłowy LAPACK i dokumentacja są łatwo dostępne.
Możesz także zajrzeć do artykułu Goto i Van De Geijna „Anatomia wysokowydajnego mnożenia macierzy” na stronie http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.140.1785&rep=rep1&type=pdf
źródło
Odpowiedź brzmi: LAPACK, a biblioteki BLAS sprawiają, że MATLAB jest oślepiająco szybki w operacjach matrycowych, a nie żaden zastrzeżony kod przez ludzi z MATLAB.
Użyj bibliotek LAPACK i / lub BLAS w kodzie C ++ do operacji na macierzach i powinieneś uzyskać podobną wydajność jak MATLAB. Biblioteki te powinny być swobodnie dostępne w każdym nowoczesnym systemie, a części zostały opracowane przez dziesięciolecia w środowisku akademickim. Należy pamiętać, że istnieje wiele implementacji, w tym niektóre zamknięte źródła, takie jak Intel MKL .
Dyskusja na temat tego, jak BLAS osiąga wysoką wydajność, jest dostępna tutaj.
BTW, z mojego doświadczenia jest poważny problem z wywoływaniem bibliotek LAPACK bezpośrednio z c (ale warto). Musisz dokładnie przeczytać dokumentację.
źródło
Podczas mnożenia macierzy używasz naiwnej metody mnożenia, która wymaga czasu
O(n^3)
.Istnieje algorytm mnożenia macierzy, który wymaga
O(n^2.4)
. Co oznacza, żen=2000
Twój algorytm wymaga ~ 100 razy więcej obliczeń niż najlepszy algorytm.Naprawdę powinieneś sprawdzić stronę wikipedii pod kątem mnożenia macierzy, aby uzyskać dodatkowe informacje na temat efektywnych sposobów jej realizacji.
źródło
W zależności od wersji Matlaba, sądzę, że może już korzystać z twojego GPU.
Inna rzecz; Matlab śledzi wiele właściwości twojej matrycy; w jej przekątnej, hermetyce i tak dalej, i specjalizuje się w oparciu o nią algorytmy. Może specjalizuje się w oparciu o macierz zerową, którą mi przekazujesz, czy coś takiego? Może buforowanie powtarzających się wywołań funkcji zaburza Twoje czasy? Być może optymalizuje powtarzające się nieużywane produkty matrycowe?
Aby uchronić się przed takimi zdarzeniami, użyj matrycy liczb losowych i upewnij się, że wymuszasz wykonanie, drukując wynik na ekranie lub dysku lub w inny sposób.
źródło
A.*B
robi. Więc OP prawie na pewno coś wygłupia.MATLAB wykorzystuje wysoce zoptymalizowaną implementację LAPACK od Intela znaną jako Intel Math Kernel Library (Intel MKL) - w szczególności funkcję dgemm . Szybkość Ta biblioteka wykorzystuje funkcje procesora, w tym instrukcje SIMD i procesory wielordzeniowe. Nie dokumentują używanego algorytmu. Jeśli miałbyś wywołać Intel MKL z C ++, powinieneś zobaczyć podobną wydajność.
Nie jestem pewien, jakiej biblioteki MATLAB używa do mnożenia GPU, ale prawdopodobnie coś takiego jak nVidia CUBLAS .
źródło
Ogólna odpowiedź na pytanie „Dlaczego matlab jest szybszy w wykonywaniu xxx niż inne programy” jest taka, że matlab ma wiele wbudowanych, zoptymalizowanych funkcji.
Inne używane programy często nie mają tych funkcji, więc ludzie stosują własne kreatywne rozwiązania, które są zaskakująco wolniejsze niż profesjonalnie zoptymalizowany kod.
Można to interpretować na dwa sposoby:
1) Powszechny / teoretyczny sposób: Matlab nie jest znacznie szybszy, po prostu źle robisz test porównawczy
2) Realistyczny sposób: w tym przypadku Matlab jest szybszy w praktyce, ponieważ języki takie jak c ++ są zbyt łatwo używane w nieskuteczny sposób.
źródło
Ostry kontrast wynika nie tylko z niesamowitej optymalizacji Matlaba (omówionej już w wielu innych odpowiedziach), ale również ze sposobu, w jaki sformułowałeś macierz jako obiekt.
Wygląda na to, że macie matrycę listę list? Lista list zawiera wskaźniki do list, które następnie zawierają elementy macierzy. Lokalizacje zawartych list są przypisywane arbitralnie. Gdy zapętlasz swój pierwszy indeks (numer wiersza?), Czas dostępu do pamięci jest bardzo znaczący. Dla porównania, dlaczego nie spróbujesz zaimplementować macierzy jako pojedynczej listy / wektora za pomocą następującej metody?
I
Należy zastosować ten sam algorytm mnożenia, aby liczba flopów była taka sama. (n ^ 3 dla macierzy kwadratowych o rozmiarze n)
Proszę o czas, aby wynik był porównywalny z tym, co miałeś wcześniej (na tej samej maszynie). Dzięki porównaniu pokażesz dokładnie, jak znaczący może być czas dostępu do pamięci!
źródło
W C ++ jest wolny, ponieważ nie używasz wielowątkowości. Zasadniczo, jeśli A = BC, gdzie wszystkie są macierzami, pierwszy rząd A można obliczyć niezależnie od drugiego rzędu itp. Jeśli wszystkie A, B i C są wszystkie n na macierzach n, możesz przyspieszyć mnożenie przez współczynnik n ^ 2, jak
a_ {i, j} = sum_ {k} b_ {i, k} c_ {k, j}
Jeśli użyjesz, powiedzmy, Eigen [ http://eigen.tuxfamily.org/dox/GettingStarted.html ], wielowątkowość jest wbudowana, a liczba wątków jest regulowana.
źródło
Ponieważ MATLAB jest początkowo językiem programowania opracowanym dla numerycznej algebry liniowej (manipulacje macierzowe), który zawiera biblioteki specjalnie opracowane do mnożenia macierzy. A teraz MATLAB może dodatkowo korzystać z GPU (procesora graficznego) .
A jeśli spojrzymy na wyniki obliczeń:
wtedy możemy zobaczyć, że nie tylko MATLAB jest tak szybki w mnożeniu macierzy: CUDA C (język programowania NVIDIA) ma lepsze wyniki niż MATLAB. CUDA C ma również biblioteki opracowane specjalnie do mnożenia macierzy i wykorzystuje procesory graficzne.
Krótka historia MATLAB
Co to jest CUDA C.
CUDA C korzysta również z bibliotek specjalnie opracowanych do mnożenia macierzy, takich jak OpenGL (Open Graphics Library). Wykorzystuje także GPU i Direct3D (w MS Windows).
Porównywanie prędkości procesora i GPU
Od wprowadzenia do przewodnika programowania CUDA C:
Zaawansowane czytanie
Podstawowe podprogramy algebry liniowej (BLAS)
Anatomia wysokowydajnego mnożenia macierzy , autorstwa Kazushige Goto i Roberta A. Van De Geijna
Kilka interesujących twarzy
źródło
"additionally"
. Oznacza to: można go używać. Oznacza to również, że normalne mnożenie macierzy nadal korzysta z bibliotek oprogramowania. Czy uważasz, że muszę zmienić swój post, aby był bardziej zrozumiały? Dziękuję za twoje komentarze!