Może się to wydawać oczywiste, ale komputery nie wykonują formuł , wykonują kod , a czas wykonania zależy bezpośrednio od kodu, który wykonują, i tylko pośrednio od jakiejkolwiek koncepcji, którą implementuje kod. Dwa logicznie identyczne fragmenty kodu mogą mieć bardzo różne charakterystyki wydajności. Niektóre powody, które mogą pojawić się w wyniku mnożenia macierzy:
- Używanie wielu wątków. Prawie nie ma nowoczesnego procesora, który nie ma wielu rdzeni, wiele ma do 8, a wyspecjalizowane maszyny do obliczeń o wysokiej wydajności mogą z łatwością mieć 64 na kilku gniazdach. Pisanie kodu w oczywisty sposób, w normalnym języku programowania, wykorzystuje tylko jeden z nich. Innymi słowy, może zużywać mniej niż 2% dostępnych zasobów obliczeniowych komputera, na którym działa.
- Korzystanie z instrukcji SIMD (myląco nazywane jest to również „wektoryzacją”, ale w innym sensie niż w cytatach tekstowych w pytaniu). Zasadniczo, zamiast 4 lub 8 skalarnych instrukcji arytmetycznych, daj CPU jedną instrukcję, która wykonuje arytmetykę na 4 lub 8 rejestrach równolegle. Może to dosłownie wykonać kilka obliczeń (gdy są one całkowicie niezależne i pasują do zestawu instrukcji) 4 lub 8 razy szybciej.
- Inteligentniejsze wykorzystanie pamięci podręcznej . Dostęp do pamięci jest szybszy, jeśli jest on spójny czasowo i przestrzennie , tzn. Kolejne dostępy są do pobliskich adresów, a podczas uzyskiwania dostępu do adresu dwukrotnie uzyskuje się do niego dwa razy z rzędu, a nie z długą przerwą.
- Korzystanie z akceleratorów, takich jak procesory graficzne. Urządzenia te różnią się znacznie od procesorów, a ich efektywne programowanie jest samodzielną formą sztuki. Na przykład mają setki rdzeni, które są pogrupowane w grupy kilkudziesięciu rdzeni, a te grupy współużytkują zasoby - dzielą kilka KiB pamięci, która jest znacznie szybsza niż normalna pamięć, a kiedy dowolny rdzeń grupy wykonuje
if
instrukcja wszyscy inni w tej grupie muszą na to poczekać.
- Rozłóż pracę na kilka komputerów (bardzo ważne w superkomputerach!), Co wprowadza ogromny zestaw nowych problemów, ale oczywiście może zapewnić dostęp do znacznie większych zasobów obliczeniowych.
- Inteligentniejsze algorytmy. W przypadku mnożenia macierzy prosty algorytm O (n ^ 3), odpowiednio zoptymalizowany przy użyciu powyższych sztuczek, jest często szybszy niż algorytmy pod-sześcienne dla rozsądnych rozmiarów macierzy, ale czasami wygrywa. W szczególnych przypadkach, takich jak rzadkie macierze, możesz pisać wyspecjalizowane algorytmy.
Wielu inteligentnych ludzi napisało bardzo skuteczny kod dla typowych operacji algebry liniowej , używając powyższych sztuczek i wielu innych, a zwykle nawet głupich sztuczek specyficznych dla platformy. Dlatego przekształcenie formuły w mnożenie macierzy, a następnie wdrożenie tego obliczenia przez wywołanie dojrzałej biblioteki algebry liniowej korzysta z tego wysiłku optymalizacji. Z drugiej strony, jeśli po prostu wypiszesz formułę w oczywisty sposób w języku wysokiego poziomu, wygenerowany kod maszynowy nie wykorzysta wszystkich tych sztuczek i nie będzie tak szybki. Jest to również prawdą, jeśli weźmiesz formułowanie macierzy i zaimplementujesz ją przez wywołanie naiwnej procedury mnożenia macierzy, którą sam napisałeś (ponownie, w oczywisty sposób).
Szybkie tworzenie kodu wymaga pracy , a często dużo pracy, jeśli chcesz uzyskać ostatnią uncję wydajności. Ponieważ tak wiele ważnych obliczeń można wyrazić jako połączenie kilku operacji algebry liniowej, ekonomicznie jest stworzyć wysoce zoptymalizowany kod dla tych operacji. Ale twój wyjątkowy przypadek specjalnego zastosowania? Nikt nie dba o to oprócz ciebie, więc optymalizacja tego nie jest ekonomiczna.