Pytanie, które co jakiś czas pojawia się na większości forum: “Czy warto pisać różne fragmenty silnika w asemblerze ?” Odpowiedź jest prosta i nie podlegająca dyskusji: Nie. W chwili obecnej kompilatory są na tyle zaawansowane, że potrafią wygenerować kod bliski idealnemu. Złożoność obecnych silników jest na tyle duża a ich konstrukcja tak często się zmienia, że ciągłe poprawianie czegoś w asemblerze byłoby zwykłym masochizmem i stratą czasu. Ponadto procesory swoją konstrukcją dążą do tego aby porządek instrukcji (Out-of-order execution, Macrofusion itp) miał coraz mniejsze znaczenie. Dużo więcej można osiągnąć wprowadzając ogólną reorganizację i zmianę algorytmów niż niskopoziomowe optymalizowanie w kodzie asemblera. Jednak jest pewna rzecz związana z konstrukcją procesora, której wciąż ze świecą szukać w komercyjnych silnikach, a którą można z powodzeniem zastosować aby uzyskać znaczny wzrost prędkości obliczeń. Chodzi tu mianowicie o instrukcje SSE. Różnorodne operacje na wektorach, macierzach, bounding box’ach, testy widoczności czy testy kolizji lub przecięć promieni, aż się proszą o implementację w SSE. Jest to w tej chwili jeszcze bardziej kuszące gdyż w najnowszej generacji procesorów Intel Core™, instrukcje SSE są wykonywane w jednym cyklu zegara a nie w 2 jak do tej pory było, więc przyrost prędkości jest dwukrotny, natomiast przechodząc z użycia FPU na użycie SSE to przyrost prędkości obliczeń jest kilku-kilkunasto krotny. Wystarczy spojrzeć chociażby na tak prostą operację, jak dodawanie dwóch wektorów. Taka operacja wymaga załadowania 3 wartości, 3 dodawań oraz 3 zapisów, łącznie 9 operacji. W przypadku SSE możemy to zrobić za pomocą 3 operacji, chociaż warto przy okazji zwrócić uwagę na fakt, że w przypadku wektora FPU mogliśmy używać 3 wartości float, podczas gdy w przypadku SSE musimy użyć wektora z 4 wartościami float. Jest to pewna „niedogodność” z której trzeba sobie zdawać sprawę i dlatego warto zawczasu tak zaprojektować silnik aby to nie stanowiło zbytniego kłopotu.

Bardzo istotną rzeczą jaką SSE umożliwia jest wyeliminowanie kosztownych i złożonych operacji za pomocą jednej instrukcji. Przykładem tego jest np. instrukcja CMPPS która umożliwia wyeliminowanie kosztownego skoku warunkowego lub też inne instrukcje typu MINPS i MAXPS, które się bardzo przydadzą podczas pisania operacji na BoundingBox’ie lub podczas implementacji OctTree. Inną pożyteczną instrukcją jest też RSQRTPS tak często używaną przy normalizacji wektora, która jest ok. 8x szybsza od swoich odpowiedników w FPU (czyli podzielenie jedynki przez pierwiastek z liczby) i która jest aż 40x szybsza w procesorach Intel Core™. Inną pozytywną rzeczą wynikającą z użycia SSE w silnikach dla PC, jest fakt, że konsole takie jak Xbox360 czy PS2/PS3 zostały zaprojektowane właśnie z myślą o pracy na danych wektorowych, tak więc silnik oparty o SSE można „przestawić” na pracę w konsolowym środowisku znacznie prościej niż robiąc to z silnikiem opartym o FPU.

Wracając jednak do tematu, to w przypadku SSE kompilatory MS oraz Intel’a zwalniają nas z konieczności używania asemblera. Do dyspozycji mamy dodatkowe typy danych takie jak __m128 oraz specjalny zestaw funkcji wykonujących złożone operacje na tych danych. Co więcej, użycie asemblera jest nawet nie wskazane, gdyż obecne kompilatory potrafią optymalizować kod również pod względem użycia SSE. Nie ograniczają się więc do zwykłego udostępnienia dodatkowych typów danych czy operacji. Potrafią one zoptymalizować nasz kod używający operacji SSE, a nawet zwykły kod zmienić tak aby korzystał on z SSE. Wystarczy odpowiednie ustawienie w projekcie kompilatora Microsoftu i już generowany kod będzie używać w niektórych sytuacjach, kodu SSE.

Tak jak w przypadku grafiki, tak i tutaj występuje “problem” kompatybilności sprzętu. SSE zostało wprowadzone mniej więcej 6 lat temu. Rozszeżenie to jest obsługiwane zarówno przez procesory Intel jak i AMD. Jeszcze niedawno zastanawiałem się czy warto posiadać 2 wersje silnika; jedną w wersji FPU drugą w wersji SSE. Dzisiaj już wiem, że nie warto. Można śmiało „inwestować” w użycie SSE, gdyż, nie oszukujmy się, procesor starej generacji nie posiadający instrukcji SSE i tak za całą pewnością „nie pociągnie” współczesnej gry, w stopniu umożliwiającym jakiekolwiek granie.

Optymalizacja cz.2. Asembler
1 vote, 5.00 avg. rating (100% score)