Dobrym punktem wyjścia jest wspaniała książka „ The Science of Programming Matrix Computations” autorstwa Roberta A. van de Geijna i Enrique S. Quintana-Ortí. Zapewniają bezpłatną wersję do pobrania.
BLAS jest podzielony na trzy poziomy:
Poziom 1 definiuje zestaw funkcji algebry liniowej, które działają tylko na wektorach. Funkcje te czerpią korzyści z wektoryzacji (np. Przy użyciu SSE).
Funkcje poziomu 2 to operacje macierzowo-wektorowe, np. Jakiś iloczyn macierzowo-wektorowy. Funkcje te można by zaimplementować w kategoriach funkcji poziomu 1. Możesz jednak zwiększyć wydajność tej funkcji, jeśli możesz zapewnić dedykowaną implementację, która wykorzystuje architekturę wieloprocesorową ze współdzieloną pamięcią.
Funkcje poziomu 3 to operacje takie jak iloczyn macierzy-macierzy. Ponownie możesz zaimplementować je w kategoriach funkcji Level2. Ale funkcje Level3 wykonują operacje O (N ^ 3) na danych O (N ^ 2). Jeśli więc Twoja platforma ma hierarchię pamięci podręcznej, możesz zwiększyć wydajność, jeśli zapewnisz dedykowaną implementację, która jest zoptymalizowana pod kątem pamięci podręcznej / przyjazna dla pamięci podręcznej . Jest to ładnie opisane w książce. Głównym wzmocnieniem funkcji Level3 jest optymalizacja pamięci podręcznej. To przyspieszenie znacznie przewyższa drugie wzmocnienie z równoległości i innych optymalizacji sprzętu.
Nawiasem mówiąc, większość (lub nawet wszystkie) wysokowydajnych implementacji BLAS NIE jest zaimplementowanych w Fortranie. ATLAS jest zaimplementowany w C. GotoBLAS / OpenBLAS jest zaimplementowany w C, a jego części krytyczne dla wydajności w Assemblerze. W Fortranie zaimplementowano tylko referencyjną implementację BLAS. Jednak wszystkie te implementacje BLAS zapewniają interfejs Fortran w taki sposób, że można go połączyć z LAPACK (LAPACK zyskuje całą swoją wydajność z BLAS).
Zoptymalizowane kompilatory odgrywają pod tym względem niewielką rolę (a dla GotoBLAS / OpenBLAS kompilator nie ma żadnego znaczenia).
IMHO żadna implementacja BLAS nie wykorzystuje algorytmów, takich jak algorytm Coppersmith-Winograd lub algorytm Strassena. Nie jestem do końca pewien, dlaczego tak jest, ale zgaduję:
- Może nie jest możliwe zapewnienie implementacji zoptymalizowanej pod kątem pamięci podręcznej tych algorytmów (tj. Straciłbyś więcej niż wygrał)
- Te algorytmy nie są stabilne numerycznie. Ponieważ BLAS jest obliczeniowym jądrem LAPACK, nie można tego zrobić.
Edycja / aktualizacja:
Nowym i przełomowym dokumentem na ten temat są dokumenty BLIS . Są wyjątkowo dobrze napisane. Na wykładzie "Podstawy oprogramowania do obliczeń o wysokiej wydajności" zaimplementowałem produkt macierzowo-macierzowy po ich artykule. Właściwie zaimplementowałem kilka wariantów produktu macierzowo-macierzowego. Najprostsze warianty są w całości napisane w czystym C i mają mniej niż 450 linii kodu. Wszystkie inne warianty tylko optymalizują pętle
for (l=0; l<MR*NR; ++l) {
AB[l] = 0;
}
for (l=0; l<kc; ++l) {
for (j=0; j<NR; ++j) {
for (i=0; i<MR; ++i) {
AB[i+j*MR] += A[i]*B[j];
}
}
A += MR;
B += NR;
}
Ogólna wydajność produktu macierz-macierz zależy tylko od tych pętli. Spędza się tu około 99,9% czasu. W pozostałych wariantach użyłem elementów wewnętrznych i kodu asemblera, aby poprawić wydajność. Możesz zobaczyć samouczek przedstawiający wszystkie warianty tutaj:
ulmBLAS: Samouczek dotyczący GEMM (produkt Matrix-Matrix)
Wraz z dokumentami BLIS dość łatwo można zrozumieć, w jaki sposób biblioteki takie jak Intel MKL mogą uzyskać taką wydajność. I dlaczego nie ma znaczenia, czy używasz pamięci głównej w postaci wierszy czy kolumn!
Końcowe testy porównawcze są tutaj (nazwaliśmy nasz projekt ulmBLAS):
Benchmarki dla ulmBLAS, BLIS, MKL, openBLAS i Eigen
Kolejna edycja / aktualizacja:
Napisałem również kilka poradników na temat tego, jak BLAS jest używany do rozwiązywania problemów z algebry liniowej numerycznej, takich jak rozwiązywanie układu równań liniowych:
Wysokowydajna faktoryzacja LU
(Ta faktoryzacja LU jest na przykład używana przez Matlab do rozwiązywania układu równań liniowych).
Mam nadzieję, że znajdę czas na rozszerzenie tego samouczka, aby opisać i zademonstrować, jak zrealizować wysoce skalowalną równoległą implementację faktoryzacji LU, jak w PLASMA .
OK, gotowe: kodowanie równoległej faktoryzacji LU zoptymalizowanej pod kątem pamięci podręcznej
PS: Zrobiłem też kilka eksperymentów nad poprawą wydajności uBLAS. W rzeczywistości jest całkiem proste, aby zwiększyć (tak, grać słowami :)) wydajność uBLAS:
Eksperymenty na uBLAS .
Tutaj podobny projekt z BLAZE :
Eksperymenty na BLAZE .