RSS

Asembler czasem się jeszcze przydaje

Liczba odsłon: 36

Niecały rok temu oznajmiłem, że wstawki asemblerowe straciły swą moc. I choć wciąż podtrzymuję tę opinię, dzisiaj podaję (kolejny już) przykład, że nie zawsze należy bezwzględnie trzymać się reguły pisania aplikacji bez wstawek asemblerowych.

Ponieważ zajmuję się ostatnio poważniej długotrwałymi obliczeniami numerycznymi (stąd też zmniejszona częstotliwość wpisów na blogu i mniej nowych materiałów na mojej stronie), zainteresowałem się zastosowaniem rozkazów SSE i SSE2, teoretycznie zdolnych do znakomitego przyspieszenia działania programu w przypadku operacji na wektorach. Krótko mówiąc, operacja na dwuelementowym wektorze liczb typu double z wykorzystaniem SSE2 trwa tyle samo czasu, co operacja na pojedynczej liczbie z wykorzystaniem standardowego koprocesora matematycznego.

Co mają do tego wstawki asemblerowe? Otóż po zaimplementowaniu technologii SSE za pomocą makropoleceń kompilatora C (tak zwane SSE intrinsics) i uzyskaniu całkiem eleganckiego przyrostu wydajności, oglądnąłem wygenerowany program asemblerowy i – stwierdziwszy jego przeciętny raczej poziom – postanowiłem parę częściej wykorzystywanych procedur zaimplementować dla porównania w asemblerze. Oto przykładowe czasy, uzyskane na komputerze z mikroprocesorem Intel Pentium 4 2.66 GHz (Prescott) dla 10 000 pętli operacji na wektorze składającym się ze 128 Ki elementów typów float (SSE) i double (SSE2):

Algorytm Typ danych FPU SSE SSE ASM
Suma elementów wektora float 2.99 s 1.70 s 0.61 s
Suma elementów wektora double 3.39 s 3.80 s 1.59 s
Porównywanie dwóch wektorów float 4.94 s 1.39 s 1.13 s

Jak widać, zastosowanie asemblera pozwoliło – w zależności od zadania – skrócić czas obliczeń dwukrotnie, ominąć problemy optymalizacji obliczeń kompilatora C lub przynajmniej zyskać kilkadziesiąt milisekund czasu tam, gdzie czynnikiem krytycznym nie była już jakość kodu, a szybkość realizacji operacji arytmetycznych lub dostępu do pamięci.