Komputery bez procesora?

Wstyczniu tego roku rosyjska firma ElcomSoft zaniepokoiła wielu użytkowników sieci bezprzewodowych Wi-Fi informacją, że udało jej się drastycznie skrócić czas łamania zabezpieczeń standardów WPA/WPA2. Innowacyjny był jednak nie algorytm, lecz zastosowana jednostka obliczeniowa: karta graficzna. Proponowane rozwiązanie pozwala generować hasła za pomocą karty Nvidia GeForce GTX 280 ponad dziesięć razy szybciej niż z wykorzystaniem czterordzeniowego procesora Intel Core 2 Quad Q6600.
ATI Radeon serii 4800 to pierwszy na świecie GPU o wydajności arytmetycznej ponad jednego teraflopa
ATI Radeon serii 4800 to pierwszy na świecie GPU o wydajności arytmetycznej ponad jednego teraflopa

Ten i wiele innych przykładów dowodzi, że procesory graficzne pod względem mocy obliczeniowej mają dużą przewagę nad głównymi procesorami. Sytuacja wygląda jeszcze bardziej dramatycznie, gdy spojrzymy na teoretyczną wydajność arytmetyczną GPU. Powszechnie stosowaną miarą jest liczba operacji zmiennoprzecinkowych, które dany układ może wykonać w ciągu sekundy (FLOPS). Najszybsze procesory Intela uzyskują wynik 96 GFLOPS, tymczasem osiągi ostatnich produktów AMD i Nvidii oscylują już wokół jednego teraflopa, czyli 1000 GFLOPS!

Oczywiście porównanie to nie miałoby większego sensu, gdyby karty graficzne w dalszym ciągu nadawały się tylko do wyświetlania grafiki w grach. Tymczasem równocześnie z szybkim wzrostem wydajności procesorów graficznych poprawia się też elastyczność i programowalność GPU. Od kilku lat rozwijana jest technika GPGPU, czyli General Purpose computing on GPU. To metoda wykorzystania kart graficznych do zastosowań ogólnych – niezwiązanych z wyświetlaniem grafiki rastrowej. Pojawia się coraz więcej aplikacji potrafiących zrobić świetny użytek z pokładów mocy układów graficznych. Czy zatem wkrótce większość zadań nasze komputery będą wykonywały za pomocą kart graficznych, a CPU zostanie zepchnięte na margines?

Historia: Jak powstał GPGPU?

Do 2000 roku proces przetwarzania danych przez karty graficzne był z góry zdefiniowany w samym sprzęcie, a więc programista tworzący aplikacje nie miał większego wpływu na jego przebieg. Z czasem jednak producenci kart graficznych dostrzegli i zaczęli uwzględniać potrzebę zwiększenia swobody artystycznej twórców gier. Aby wyglądały one ciekawiej, a karty sprzedawały się lepiej, zdecydowano o wprowadzeniu programowalnych jednostek cieniujących.

W 2001 roku wraz z rodziną GeForce 3 pojawiły się Vertex Shader 1.1 oraz Pixel Shader 1.1 (zbiorczo określane jako Shader Model 1.1 i zaimplementowane w DirectX 8.0). Cieniowanie wierzchołków (VS 1.1) pozwoliło na dowolne modyfikowanie właściwości wierzchołków obiektów trójwymiarowych (np. pozycji, koloru, oświetlenia, współrzędnych tekstur). Dzięki temu możliwe stało się na przykład modyfikowanie geometrii obiektów oraz wydajne dodawanie niektórych efektów (np. mgły). Z kolei cieniowanie pikseli (PS 1.1) pozwoliło na modyfikowanie poszczególnych pikseli otrzymanych w procesie rasteryzacji geometrii. Umożliwiło to implementację niedostępnych wcześniej rodzajów cieniowania, filtrów itp.

Jednocześnie narodziła się koncepcja wykorzystania kart graficznych do obliczeń niezwiązanych z grafiką. Jednak możliwości programowania były w tamtym momencie jeszcze bardzo ograniczone. Kod wykonywany przez jednostki cieniujące mógł liczyć najwyżej kilkadziesiąt instrukcji, zaś zestaw dostępnych instrukcji ograniczał się do podstawowych funkcji matematycznych i operacji odczytu tekstur. W kolejnych generacjach kart graficznych dokonywał się niewielki postęp.

Przełom nastąpił w 2004 roku – wraz z pojawieniem się rodziny GeForce 6 wprowadzającej obsługę Shader Model 3.0 (DirectX 9.0c). Wreszcie do dyspozycji było podstawowe narzędzie programisty: sterowanie przebiegiem programu (flow control), czyli pętle, skoki i instrukcje warunkowe. Jednocześnie limit długości kodu został zwiększony do 512 instrukcji. Dzięki temu wizja GPGPU stała się bardzo realna. Zaczęły powstawać prace naukowe eksplorujące wydajność GPU lub dowodzące jej w zadaniach, których dotąd nie wiązano z produktami Nvidii i ATI (obecnie AMD). GPGPU stało się ważnym tematem konferencji SIGGRAPH, poświęconej grafice komputerowej. Podjęto też próby wykorzystania GPGPU do celów komercyjnych.

Wreszcie pod koniec 2006 roku pojawiła się seria układów GeForce 8 z obsługą Shader Model 4.0 zawartego na przykład w DirectX 10. Zniknął podział na jednostki cieniowania wierzchołków i pikseli, tym samym rozwiązano problem nierównego podziału pracy między nimi. Zastąpiły je jednostki zunifikowane (unified shaders wg ATI), zwane też procesorami strumieniowymi (stream processors wg Nvidii). Umożliwiono również swobodny zapis danych w pamięci karty graficznej (scatter), a limit długości kodu zwiększono do 65 tysięcy instrukcji.

Nie sposób nie wspomnieć o technologii CUDA wprowadzonej przez Nvidię wkrótce potem (luty 2007 r.). Dopiero ona uwolniła programistów od “graficznego” sposobu operowania, czyli przemycania danych w teksturach, wywoływania kodu na pikselach, graficznych interfejsów API (DirectX, OpenGL) i graficznych języków (Cg, GLSL). Udostępniła natomiast wygodne środowisko programowania w powszechnie znanym języku C.

Z podobną inicjatywą wyszła firma ATI. Najpierw pojawił się projekt Close-To-Metal (CTM), czyli niskopoziomowe API przeznaczone do GPGPU. Ponieważ jednak narzędzie to było trudne w użyciu, ATI zaadaptowało Brook+, czyli oparty na C język i kompilator stworzony

na Uniwersytecie Stanforda. Efekty pracy ATI w dziedzinie GPGPU znane są pod nazwą Stream.

Wraz z serią procesorów graficznych GeForce 200 wprowadzone zostały ostatnie usprawnienia, czyli bardziej swobodny dostęp do pamięci oraz obsługa liczb podwójnej precyzji. To ostatnie to ewidentny ukłon w stronę środowiska GPGPU, gdyż 64–bitowa precyzja nie jest potrzebna w grach, natomiast często jest niezbędna w profesjonalnych zastosowaniach numerycznych.

Obecnie terminy “GPGPU” i “CUDA” co chwila pojawiają się w mediach branżowych. Wiele najważniejszych uczelni technicznych na świecie ma tematykę GPGPU w programie nauczania oraz zajmuje się badaniami dotyczącymi tego rozwiązania. Najwięksi twórcy oprogramowania usprawniają działanie popularnych aplikacji, powierzając najtrudniejsze zadania właśnie kartom graficznym. Zaś producenci GPU zaczynają umniejszać znaczenie głównych procesorów pecetów.

Architektury: Czym różni się CPU od GPU?

Procesory komputerów osobistych zawsze były rozwijane z myślą o wydajnym wykonywaniu dowolnego kodu. Tak powstały układy z pojedynczymi, ale skomplikowanymi rdzeniami zdolnymi do bardzo szybkiego wykonywania ciągów instrukcji.

Tymczasem karty graficzne od początku miały wykonywać relatywnie proste obliczenia na dużej ilości danych (wierzchołków i tekstur). Z tego względu zastosowano wiele jednostek obliczeniowych pracujących obok siebie – aż 240 w GeForce 280 GTX. Żeby jednak możliwe było zgromadzenie dużej liczby jednostek obliczeniowych w jednym procesorze graficznym, musiały one pozostać relatywnie nieskomplikowane. Było to konieczne, by utrzymać w rozsądnych granicach wielkości chipów, ich ceny i zużycie energii. Niestety, z prostoty konstrukcji wynika też ograniczenie uniwersalności obliczeniowej GPU. Aby zrozumieć różnice w działaniu i możliwościach tych układów, przyjrzymy się ich architekturom.

CPU, czyli procesor

Aby przyspieszyć wykonanie instrukcji, dekady temu projektanci procesorów wprowadzili mechanizm potoku wykonawczego (instruction pipeline). Potokowość można porównać do produkcji taśmowej w fabryce. Dzięki podziałowi pracy na wiele małych etapów produkty zjeżdżają z taśmy o wiele szybciej niż w procesie wytwarzania jednego produktu od początku do końca. Podobnie wykonanie instrukcji podzielone jest na wiele stopni. I tak jak w przypadku taśmy produkcyjnej, wydajność przetwarzania instrukcji zależy od czasu, jaki zajmuje wykonanie najwolniejszego stopnia w potoku. W skutek podziału na na coraz mniejsze i liczniejsze stopnie ten czas się skraca, a więc zwiększa się częstotliwość pracy. Dla przykładu Core 2 ma potok 14-stopniowy, zaś Core i7 – 20-stopniowy. Rekordzistami, jeśli chodzi o długość potoków, były ostatnie modele Pentium 4 – miały 31-stopniowe potoki. Jednak ze względu na problemy z odprowadzaniem ciepła zrezygnowano z tak długich potoków.

Podstawową wadą potokowości jest jej zachowanie podczas wykonywania skoków warunkowych (np. if-then-else). Dopóki instrukcja warunkowa nie przejdzie przez cały potok, nie wiadomo, jaką drogę obierze program, a zatem nie można wczytywać kolejnych instrukcji do potoku. Rozwiązaniem jest wykonanie spekulatywne. To znaczy, że jeśli jesteśmy w stanie przewidzieć, jak zachowa się program, możemy od razu wczytywać instrukcje z tego wariantu, zapobiegając przestojom. Niestety, jeśli źle zgadniemy, cały potok trzeba oczyścić i wczytać instrukcje z alternatywnej gałęzi programu. Jest to bardzo kosztowne, dlatego skuteczny mechanizm przewidywania skoków (branch prediction) ma duże znaczenie dla wydajności.  Istnieją dwa podstawowe jego warianty: statyczny i dynamiczny. Statyczny opiera się na założeniach, np. że skoki wstecz zazwyczaj są wykonywane, zaś skoki wprzód wręcz przeciwnie. Dynamiczny opiera się na tworzonej na bieżąco tabeli historycznej skoków wykonanych wcześniej przez program. Tabela oczywiście wymaga własnej pamięci i wraz ze złożoną logiką branch prediction przekłada się na więcej tranzystorów i większą powierzchnię krzemu. Jest to sprzeczne z ekonomią budowy GPU. Dlatego procesory graficzne nie przewidują skoków wcale lub dokonują przewidywań w niewielkim stopniu.

Jak się przekonamy, przyczyną przestojów mogą być też odczyty danych z pamięci. Rozwiązaniem jest mechanizm wykonania poza kolejnością (out-of-order execution). Jeśli zależności między instrukcjami na to pozwalają, można wstrzymać daną instrukcję i wykonywać kolejne, a wstrzymaną wznowić, gdy pojawią się oczekiwane dane. Ostatnim zagadnieniem związanym z potokowością jest superskalarność. Potoki rozbudowuje się nie tylko poprzez wydłużanie, ale i poszerzanie. Innymi słowy, do potoków można dodawać więcej jednostek wykonawczych albo wręcz powielać całe potoki, co pozwala na wykonywanie więcej niż jednej instrukcji w jednym takcie zegara. Nie należy tego jednak mylić z wielordzeniowością, która umożliwia równoległy bieg osobnych wątków – zupełnie różnych ciągów instrukcji. Procesor superskalarny pozwala na przyspieszenie wykonania konkretnego wątku poprzez równoległe wykonanie kilku kolejnych instrukcji, jeśli tylko pozwalają na to zależności między nimi.

Obok wyżej opisanych mechanizmów usprawniających wykonanie instrukcji trzeba wymienić hierarchię pamięci procesora CPU. Zanim przejdziemy do szczegółów, posłużmy się przykładem prostego procesora o taktowaniu 2 GHz. Wyobraźmy sobie, że ma on wykonać serię operacji dodawania, przy czym do każdej z nich potrzebna jest dana, którą trzeba wczytać z pamięci. Jeśli założymy, że odczyt z pamięci zajmuje 199 cykli zegara, oznaczać to będzie, że po każdej operacji dodawania procesor będzie bezczynnie oczekiwał przez czas, w którym teoretycznie mógłby wykonać 199 instrukcji! Krótko mówiąc: jego efektywna szybkość wyniesie… 10 MHz.

Niestety, czasy dostępu do pamięci RAM współczesnych komputerów mieszczą się w przedziale 100–300 cykli CPU. Dlatego niezbędne jest użycie cache’u, czyli małej, ale bardzo sprawnej pamięci podręcznej wbudowanej w sam procesor. Pozwala ona na szybki dostęp do często wykorzystywanych danych. Niestety, im jest ona większa, tym droższa, ale i wolniejsza. Dlatego w dzisiejszych CPU stosuje się co najmniej dwie warstwy cache’u. W ten sposób otrzymujemy rozbudowaną hierarchię pamięci, którą można zilustrować za pomocą piramidy. Na szczycie znajdują się rejestry procesora, czyli komórki pamięci pozwalające zapisać raptem kilkadziesiąt liczb, ale za to dostępne natychmiast. Następnie mamy bardzo szybki (3–4 cykle procesora) cache pierwszego poziomu (L1), zwykle wielkości 64 kB. Niżej jest cache L2, dostępny w ciągu kilkunastu cykli procesora (np. 14 w przypadku Core 2) i mający od 256 lub 512 kB (dla CPU z cache’em L3) do 2 lub 3 MB na rdzeń (CPU bez L3). Niektóre procesory (Phenom, Core i7) posiadają jeszcze relatywnie powolny (40-50 cykli) cache L3 wspólny dla wszystkich rdzeni i mający 2 do 12 MB. Ostatni poziom w piramidzie stanowi bardzo duża, liczona w gigabajtach, ale bardzo powolna pamięć RAM. Statystycznie co trzecia instrukcja wykonywana przez procesor to operacja pamięciowa. Zatem możliwość szybkiego dostępu do danych jest nie do przecenienia.

GPU, czyli układ graficzny

Mamy już wyobrażenie o tym, co sprawia, że procesory Intela i AMD są bardzo wydajne podczas przetwarzania pojedynczych wątków. Zobaczmy zatem, jakie są mocne i słabe strony układów AMD i Nvidii w roli procesorów do zadań o charakterze ogólnym.

Zacznijmy od tego, w jaki sposób układ graficzny bywa zaprzęgany do obliczeń. Kod nie jest wykonywany na GPU autonomicznie, ale przydzielany przez program działający na CPU. Najczęściej wygląda to tak, że programista wybiera z aplikacji stosunkowo niewielki, ale powtarzalny fragment, który stanowi gros obciążenia procesora, i zmienia go na kod wykonywalny (przez GPU). Taki fragment nazywany jest kernelem. Aplikacja wywołuje kernel na wszystkich procesorach strumieniowych (SP) układu graficznego jednocześnie – tym samym otrzymujemy wiele niezależnych wątków wykonujących ten sam kod. Taką architekturę Nvidia określa jako SIMT (single instruction, multiple threads).

Teraz, aby zrozumieć funkcjonowanie procesora graficznego, zajrzymy do wnętrza GeForce 280 GTX. Układ ten podzielony jest na 10 klastrów obliczeniowych TPC (Thread Processing Cluster). Wspólnie użytkują one 256 kiB pamięci podręcznej drugiego poziomu przeznaczonej tylko do odczytu. Do komunikacji z główną pamięcią karty graficznej wykorzystywanych jest osiem wbudowanych kontrolerów pamięci. Jej przepustowość, wynosząca 141,7 GB/s, prezentuje się imponująco w porównaniu z przepustowością typowego CPU (ok. 10 GB/s), ale tylko na pierwszy rzut oka. Gdy zwrócimy uwagę na to, że ma ona dostarczyć dane aż do 240 jednostek obliczeniowych, przewaga przestaje być taka oczywista.

Każdy TPC zawiera trzy multiprocesory strumieniowe (SM), które używają wspólnej pamięci podręcznej pierwszego poziomu. Ma ona 24 kB na TPC i również jest tylko do odczytu. Globalny układ zarządzania wątkami czuwa, by żadnemu SM nie brakowało pracy. Każdy SM zawiera osiem procesorów strumieniowych (SP), które są podstawową jednostką obliczeniową GPU. Są to skalarne, zmiennoprzecinkowe jednostki arytmetyczno-logiczne pojedynczej precyzji. Obok ośmiu SP procesor SM zawiera również dwie jednostki specjalne (special function units, SFU), wykonujące bardziej złożone funkcje matematyczne (np. pierwiastek, sinus). Wreszcie każdy SM ma także jedną jednostkę arytmetyczną podwójnej precyzji. Celem ułatwienia współpracy między wątkami wykonywanymi przez SP każdy SM zawiera 16 kB pamięci współdzielonej (shared memory).

Nad pracą wszystkich jednostek w ramach SM czuwa instruction unit (IU). Grupuje on wątki w pakiety (po 32), czyli tak zwane warpy, i zarządza ich działaniem na SP. Obsłużenie całego warpa zwykle zajmuje 4 cykle zegara (32 wątki na 8 SP). Niestety, w danym momencie wszystkie wątki warpa muszą wykonywać ten sam ciąg instrukcji. Oznacza to, że jeśli wątki rozgałęzią się po napotkaniu instrukcji warunkowej, te należące do jednej gałęzi zostaną wstrzymane do czasu wykonania drugiej gałęzi – i odwrotnie. Powoduje to bezczynność części procesorów strumieniowych, a zatem duże straty wydajności. Jest to jeden z podstawowych problemów dotyczących GPGPU, ponieważ programy o charakterze ogólnym są o wiele bardziej złożone logicznie od shaderów wykorzystywanych w grafice gier.

Drugim problemem jest struktura pamięci. Jeśli potrzebne dane nie znajdują się

w malutkiej pamięci współdzielonej, to powstaje konieczność bardzo kosztownego (600 cykli) sięgnięcia do pamięci głównej. W dodatku cache tutaj zbytnio nie pomaga, ponieważ w przeciwieństwie do cache’u w CPU nie gwarantuje on krótkich czasów dostępu. Podstawowym zadaniem pamięci podręcznej w GPU jest zmniejszenie obciążenia pamięci głównej. Na szczęście istnieją mechanizmy, które częściowo rozwiązują problem szybkości dostępu do pamięci.

Po pierwsze każdy SM może zarządzać łącznie 32 warpami, a zatem 1024 wątkami (32×32). Jeśli jakiś warp utknie, ponieważ jeden z wątków czeka na wczytanie danej, to SM jest w stanie natychmiast dokonać przełączenia kontekstu na inny warp i kontynuować pracę. Gdy oczekiwane dane zostaną dostarczone z pamięci, pierwotny warp wraca z powrotem do jednostek obliczeniowych i jest przetwarzany dalej. Jeśli więc operacje pamięciowe nie są zbyt częste, można je całkowicie ukryć dzięki zamianie warpów.

Drugim mechanizmem jest łączenie operacji pamięciowych (coalescence). Jeśli kilka wątków z tego samego warpa sięga po dane znajdujące się w jednym segmencie pamięci, to odczytu można dokonać w ramach jednej operacji. Pozwala to zmniejszyć liczbę kosztownych dostępów do pamięci.

Teraz możemy skonfrontować architektury procesorów głównych i kart graficznych. Złożony potok, superskalarność oraz przewidywanie rozgałęzień sprawiają, że rdzenie CPU potrafią z ogromną szybkością przetwarzać jeden ciąg instrukcji (wątek). W dodatku mają wielowarstwowy cache, który pozwala im ograniczyć do minimum wszelkie opóźnienia w dostępie do danych. Tymczasem procesory graficzne ogromną wydajność uzyskują dzięki równoległemu przetwarzaniu na wielu jednostkach obliczeniowych. Przy tym szybkość przetwarzania poszczególnych wątków nie jest priorytetem, a większość opóźnień w dostępie do danych jest ukrywana przez przełączanie się na inne wątki.

Rezultaty takich rozwiązań konstrukcyjnych prezentujemy w ramce “Wykorzystanie krzemu”. W CPU elementy obliczeniowe ALU i FPU (kolor brązowy) stanowią tylko niewielką część procesora, zaś resztę kosztownej powierzchni krzemu zajmują omówione wcześniej mechanizmy (kolor niebieski) i cache (kolor pomarańćzowy). Tymczasem w GPU lwią część układu przeznaczono na procesory strumieniowe (kolor brązowy).

Zastosowania: GPU nie nadaje się do wszystkiego

Korzystanie z dużej liczby prostych jednostek obliczeniowych powoduje, że zmniejsza się zakres zadań, które mogą być wykonywane przez GPU. Najbardziej opłaca się implementować algorytmy, w których:

1. Operuje się na dużej ilości danych, których przetwarzanie można uczynić równoległym. To znaczy obróbka poszczególnych fragmentów zbioru danych jest niezależna od pozostałych lub zależność ta jest niewielka.

2. Przetwarzanie powinno opierać się głównie na obliczeniach (instrukcjach arytmetycznych) i nie powinno wymagać bardzo częstych dostępów do pamięci.

3. Logika algorytmów nie może być zbyt złożona, gdyż wszelkie instrukcje warunkowe i pętle znacznie obniżają wydajność przetwarzania.

Mimo wielkiej surowej mocy obliczeniowej, GPU są mało elastyczne. Ich mocną stroną jest natomiast relatywnie nieskomplikowany przemiał danych. Tymczasem atutami CPU są wszechstronność oraz szybkość przetwarzania pojedynczych wątków o dowolnej złożoności.

Dotychczas GPGPU zastosowano w praktyce m.in. w przetwarzaniu obrazów (kompresja, filtrowanie), finansach (wycena instrumentów pochodnych i opcji), poszukiwaniu złóż (dane sejsmiczne), medycynie (dane z rezonansu magnetycznego, symulacja interakcji białek) czy fizyce (interakcje ciał astronomicznych, pola elektromagnetyczne). W dziedzinach tych mamy do czynienia z ogromnymi ilościami danych, których choćby częściowe przetworzenie na GPU skutkuje dużym wzrostem wydajności (w porównaniu z rozwiązaniami opartymi tylko na CPU). Jednocześnie niemal we wszystkich zastosowaniach natrafimy na barierę jakościową, wynikającą ze wspomnianych ograniczeń GPU i zmuszającą do użycia prostszych algorytmów i struktur danych.

GPGPU zaczyna się również rozprzestrzeniać na rynku konsumenckim. Dziś każdy może sobie pobrać prostą, darmową aplikację konwertującą pliki wideo i przekonać się o kilkukrotnym wzroście wydajności. Ciekawsze jest jednak rosnące zainteresowanie producentów gier tym rozwiązaniem. Najbardziej oczywisty przykład to technologia PhysX, pozwalająca na symulację niektórych zjawisk fizycznych. Osoby posiadające nowsze karty graficzne Nvidii mogą z niej skorzystać np. w Mirror’s Edge. W tej grze PhysX symuluje (z różnym skutkiem) takie zjawiska, jak falowanie tekstyliów, tłuczenie szkła czy dym rozwiewany przez wiatr. Rzecz jasna, obliczenia te stanowią konkurencję dla podstawowego zadania GPU: wyświetlania grafiki. Testy Mirror’s Edge wykazują, że włączenie PhysX w przypadku karty GF 8800 GTX powoduje spadek średniej liczby klatek na sekundę z 80 do 50. To jednak drobnostka w porównaniu z wynikiem, jaki osiągniemy, próbując uruchomić PhysX na CPU. Wówczas uzyskamy ok. 10 fps.

Skoro już mowa o grach, to w bliższej perspektywie GPGPU można wykorzystać jeszcze przynajmniej w jednej dziedzinie. Ocenia się, że algorytmy widoczności oraz znajdywania ścieżek mogą stanowić nawet 90 procent obliczeń związanych ze sztuczną inteligencją gier. Tymczasem oba te zadania nadają się do równoległego przetwarzania na kartach graficznych. Wyobraźmy sobie, o ile bardziej wyrafinowana mogłaby być sztuczna inteligencja w grach, gdyby nie trzeba było przeznaczać tyle mocy obliczeniowej CPU na wytyczanie dróg, po których poruszają się jednostki.

Potencjał obliczeniowy procesorów graficznych zaczynają też dostrzegać producenci oprogramowania, tacy jak Adobe czy Autodesk. Na przykład w wersji CS4 pakietu graficznego Adobe Creative Suite technologię CUDA wykorzystuje się do usprawniania wielu operacji: począwszy od filtrów w Photoshopie po kodowanie wideo w Premiere.

Można by jednak zapytać, dlaczego adaptacja GPGPU nie jest szybsza. Pierwszą przeszkodą okazuje się trudność w programowaniu. Choć CUDA czyni wykorzystanie GPGPU nieporównanie łatwiejszym niż dawniej, to wciąż istnieje potrzeba zrozumienia przez programistę, w jaki sposób działa procesor graficzny. Podczas projektowania aplikacji trzeba rozdzielić pracę na równoległe wątki. Pisząc kod, powinniśmy brać poprawkę na ziarnistość przetwarzania (np. warpy) oraz specyficzny model pamięci. A jeśli chcemy, żeby program działał jak najwydajniej, trzeba ostrożnie dysponować zasobami (rejestrami, pamięcią współdzieloną itp.).

Drugi problem dotyczy uniwersalności narzędzi. Obecnie jedynym wygodnym i dobrze udokumentowanym środowiskiem GPGPU jest CUDA. Niestety, CUDA jest własnością Nvidii i działa tylko na jej kartach graficznych. A żaden producent oprogramowania nie może sobie pozwolić na to, żeby jego aplikacja działała na sprzęcie tylko jednej firmy. Dlatego, jeśli CUDA jest wykorzystywana, to wyłącznie jako opcja. Świetnym przykładem jest Mirror’s Edge. Fizyka w tej grze nie ma kluczowego wpływu na przebieg rozgrywki, a jedynie urozmaica doświadczenia gracza. Zatem posiadacz karty AMD może po prostu wyłączyć PhysX i nadal doskonale się bawić. Na aplikacje, których najważniejsze funkcje będą oparte na GPGPU, musimy jeszcze poczekać.

Aby możliwe stało się wykorzystanie GPGPU na szeroką skalę, potrzebna jest standaryzacja. Ważnym krokiem w tym kierunku jest opublikowana w grudniu specyfikacja Open Computing Language. OpenCL to otwarte środowisko do pisania programów przetwarzanych równolegle na niejednorodnych zasobach obliczeniowych. OpenCL pozwoli wykonywać zadania na wielordzeniowych CPU, na GPU dowolnego producenta, ale też na procesorach Cell, a nawet na cyfrowych procesorach sygnałowych (DSP). Choć OpenCL nie pojawił się jeszcze na rynku, to najważniejsi gracze zapewniają o pełnym zaangażowaniu. Apple, czyli inicjator OpenCL, zapowiada wsparcie dla tej technologii w następnej wersji OS X – Snow Leopard.

Przyszłość: Larrabee i kolejne generacje GPU

Już całkiem niedługo możemy się spodziewać publikacji kolejnego ważnego API – DirectX 11. Uwzględniono tu wreszcie istnienie GPGPU i wprowadzono Compute Shader.  Niestety, wygląda na to, że mamy do czynienia tylko z kopią możliwości oferowanych przez CUDA. Pocieszające jest, że otrzymamy interfejs, za pomocą którego będzie można swobodnie się porozumieć z kartami się AMD, jak i Nvidii. Wadą nadal będzie nietypowy język programowania (HLSL), z którym oswojeni są tylko programiści pracujący wcześniej z DirectX. To sprawi, że DX11 nie będzie konkurencją dla OpenCL poza światem gier, bo zdecydowana większość programistów GPGPU wybierze bardziej uniwersalną platformę.

Godny uwagi jest też zapowiadany powrót Intela na rynek procesorów graficznych. Nowy układ o nazwie Larrabee ma mieć architekturę zupełnie inną niż produkty AMD i Nvidii.  Ma się składać z wielu (8 do 48) rdzeni podobnych do rdzeni pierwszych procesorów Pentium. Wykorzystanie mającego 16 lat projektu P54C pozwoli na kompromis między charakterystyczną dla x86 elastycznością a relatywnie prostą – jak na CPU – budową. Dzięki dużo niższemu niż przed laty procesowi technologicznemu (45–32 nm zamiast 600 nm) możliwe będzie upakowanie bardzo wielu rdzeni na jednym chipie.

Rdzenie Larrabee mają być superskalarne, o poczwórnym SMT, ale niezdolne do wykonywania instrukcji poza kolejnością. Natomiast mają być zdolne do wykonywania instrukcji wektorowych podobnych do SSE (na wektorach o szerokości 16 liczb 32-bitowych). Podobnie jak inne współczesne procesory, Larrabee ma być wyposażony w 2-warstwowy cache oraz obsługiwać zestaw instrukcji x86–64. Jako GPU będzie zaopatrzony w tylko jedną sprzętową funkcję graficzną: sampler tekstur. Natomiast wszelkie inne funkcje, włącznie z rasteryzacją, mają być realizowane programowo. Przedstawiciele Nvidii próbują bagatelizować zagrożenie ze strony Intela na rynku kart graficznych. My radzimy poczekać z ocenami do pojawienia się produktu i wyników pierwszych benchmarków. Potencjalna moc, a zarazem elastyczność tak wielu rdzeni x86 z pewnością wprawi w zachwyt każdego entuzjastę GPGPU. Na pojawienie się Larrabee na rynku trzeba będzie jednak poczekać przynajmniej pół roku. W tym czasie jeszcze wiele może się wydarzyć.

Pozostaje pytanie, co będzie z CPU, gdy procesory graficzne staną się jeszcze bardziej uniwersalne. Częściową odpowiedzią na to pytanie jest strategia Intela. Mimo że szykuje się on do wypuszczenia układu graficznego wręcz stworzonego z myślą o GPGPU, to jednak nie rezygnuje z  rozwoju standardowych CPU. Wręcz przeciwnie, w najnowszym Core i7 rozbudowano wiele skomplikowanych mechanizmów do poziomu dotychczas niespotykanego. Wprowadzono drugi poziom tabeli historycznej instrukcji warunkowych oraz drugi poziom bufora translacji adresów pamięci (TLB). Zaadaptowano także rozwiązania stosowane wcześniej przez AMD, czyli wbudowany kontroler pamięci i trzeci poziom cache’u. Innymi słowy, CPU chętnie oddają kartom graficznym prostsze, ale pracochłonne zadania, a same stają się jeszcze lepsze w tym, w czym już są dobre. Wygląda więc na to, że GPU przyjmą rolę potężnych koprocesorów u boku CPU. Zaś prawdziwa walka rozegra się między Nvidią, AMD i Intelem na polu programowalnych procesorów graficznych następnej generacji.

Wydajność CPU kontra GPU

Pod względem wydajności arytmetycznej (FLOPS) karty graficzne już dawno zostawiły CPU w tyle. Wraz z premierą najnowszych układów graficznych przewaga GPU nad procesorami stanie się jeszcze większa.

Wykorzystanie krzemu

W procesorach graficznych (GPU) blisko 80% tranzystorów przeznaczono na jednostki obliczeniowe (shadery). W przypadku zwykłych procesorów (CPU) większą część  tranzystorów zabiera pamięć podręczna drugiego i trzeciego poziomu oraz mechanizmy sterujące wykonaniem.

Hierarcha pamięci CPU

Cache często pozwala uniknąć długiego oczekiwania na dane z pamięci RAM.

Rodzaj pamięciPojemnośćCzas dostępu
rejestry procesora< 1 kB0 cykli CPU
cache 1. poziomu64 kB3–4 cykle
cache 2. poziomu256–512 kB11–15 cykli
cache 3. poziomu2–12 MB40–50 cykli
pamięć RAM> 1 GB100–300 cykli

Wnętrze GeForce 280 GTX

Procesor Nvidii zawiera 10 jednostek TPC, z których każda ma 3 SM (na schemacie obok). W każdym SM jest 8 SP. W ten sposób otrzymujemy aż 240 procesorów strumieniowych.

Zastosowania procesorów graficznych

GPGPU to nie tylko fizyka w grach. Technologię tą można wykorzystać na przykład do przyspieszenia obliczeń związanych z projektowaniem infrastruktury telefonii komórkowej. Na ilustracji po lewej stronie widać symulację rozchodzenia się fal radiowych w centrum Monachium. Wykonanie obliczeń na dwurdzeniowym Athlonie 64 X2 4600+ zajęło 72 minuty. To samo zadanie karta GeForce 8600 GT wykonała w 43 sekundy, zaś układ Tesla C870 w 8 sekund.

Badanie propagacji sygnału radiowego w przestrzeni miejskiej.

Badanie propagacji sygnału radiowego w przestrzeni miejskiej.
Efekty fizyczne wzbogacają doznania wizualne, ale nie mają większego znaczenia dla rozgrywki.

Efekty fizyczne wzbogacają doznania wizualne, ale nie mają większego znaczenia dla rozgrywki.
Sprzęt do GPGPU

Choć CUDA i Stream działają na zwykłych kartach graficznych Nvidii i AMD, obie firmy oferują także produkty przeznaczone wyłącznie do GPGPU. Przedstawione poniżej GeForce 285 GTX i Tesla C1060 zbudowano na bazie tego samego GPU. Niemniej odróżnia je kilka szczegółów, jak taktowanie czy pamięć – Tesla ma 4 GB, a GeForce tylko 1 GB. Natomiast Tesla S1070 to po prostu cztery układy C1060 upakowane w obudowie 1U przeznaczonej do szaf serwerowych.