MASARYKOVA UNIVERZITA FAKULTA INFORMATIKY. programovatelných GPU a CPU (grafických procesorů

Podobné dokumenty
Co je grafický akcelerátor

13 Barvy a úpravy rastrového

Princip funkce počítače

Strojový kód. Instrukce počítače

Operační systémy. Jednoduché stránkování. Virtuální paměť. Příklad: jednoduché stránkování. Virtuální paměť se stránkování. Memory Management Unit

C2115 Praktický úvod do superpočítání

Profilová část maturitní zkoušky 2013/2014

Přednáška. Správa paměti II. Katedra počítačových systémů FIT, České vysoké učení technické v Praze Jan Trdlička, 2012

Profilová část maturitní zkoušky 2017/2018

OPS Paralelní systémy, seznam pojmů, klasifikace

Úvod. Programovací paradigmata

Představení a vývoj architektur vektorových procesorů

Čtvrtek 8. prosince. Pascal - opakování základů. Struktura programu:

Vyhledávání. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava. Prezentace ke dni 21.

Fakulta informačních technologíı. IZG cvičení 6. - Zobrazování 3D scény a základy OpenGL 1 / 38

Globální matice konstrukce

Systém adresace paměti

Přehled paralelních architektur. Dělení paralelních architektur Flynnova taxonomie Komunikační modely paralelních architektur

Základní pojmy. Program: Algoritmus zapsaný v programovacím jazyce, který řeší nějaký konkrétní úkol. Jedná se o posloupnost instrukcí.

Počítač jako prostředek řízení. Struktura a organizace počítače

Obecné výpočty na GPU v jazyce CUDA. Jiří Filipovič

5 Přehled operátorů, příkazy, přetypování

Architektury počítačů a procesorů

Pohled do nitra mikroprocesoru Josef Horálek

Jak do počítače. aneb. Co je vlastně uvnitř

8 Třídy, objekty, metody, předávání argumentů metod

Nvidia CUDA Paralelní programování na GPU

Návod k použití softwaru Solar Viewer 3D

Správné vytvoření a otevření textového souboru pro čtení a zápis představuje

Data v počítači. Informační data. Logické hodnoty. Znakové hodnoty

Úvod do GPGPU J. Sloup, I. Šimeček

PŘEDSTAVENÍ GRAFICKÉHO PROCESORU NVIDIA G200

Operační systémy. Cvičení 3: Programování v C pod Unixem

Zobrazovací jednotky a monitory

1 Nejkratší cesta grafem

Obsah. Předmluva 13 Zpětná vazba od čtenářů 14 Zdrojové kódy ke knize 15 Errata 15

Gymnázium Vysoké Mýto nám. Vaňorného 163, Vysoké Mýto

Algoritmizace a programování

Architektura počítačů

Středoškolská technika SCI-Lab

Jak v Javě primitivní datové typy a jejich reprezentace. BD6B36PJV 002 Fakulta elektrotechnická České vysoké učení technické

Základní principy zobrazení čísla Celá čísla s pevnou řádovou čárkou Zobrazení reálných čísel Aritmetika s binárními čísly

Algoritmizace a programování

Paměť počítače. alg2 1

Přednáška. Vstup/Výstup. Katedra počítačových systémů FIT, České vysoké učení technické v Praze Jan Trdlička, 2012

Opakování programování

Projektč.3dopředmětuIZP. Maticové operace

TÉMATICKÝ OKRUH Softwarové inženýrství

MATURITNÍ OTÁZKY ELEKTROTECHNIKA - POČÍTAČOVÉ SYSTÉMY 2003/2004 PROGRAMOVÉ VYBAVENÍ POČÍTAČŮ

Jan Nekvapil ČESKÉ VYSOKÉ UČENÍ TECHNICKÉ V PRAZE Fakulta elektrotechnická

VISUAL BASIC. Přehled témat

Úvod do programování - Java. Cvičení č.4

Systémy pro sběr a přenos dat

Paměťový podsystém počítače

Iterační výpočty. Dokumentace k projektu č. 2 do IZP. 24. listopadu 2004

Přednášky o výpočetní technice. Hardware teoreticky. Adam Dominec 2010

ZÁKLADY PROGRAMOVÁNÍ. Mgr. Vladislav BEDNÁŘ /14

Profilová část maturitní zkoušky 2014/2015

Architektury počítačů

Principy počítačů I Netradiční stroje

7 Formátovaný výstup, třídy, objekty, pole, chyby v programech

Další aspekty architektur CISC a RISC Aktuálnost obsahu registru

Programátorská dokumentace

Paralení programování pro vícejádrové stroje s použitím OpenMP. B4B36PDV Paralelní a distribuované výpočty

Vyhledávání. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava. Prezentace ke dni 12.

Bitové operátory a bitová pole. Úvod do programování 2 Tomáš Kühr

Zpráva o průběhu přijímacího řízení na vysokých školách dle Vyhlášky MŠMT č. 343/2002 a její změně 276/2004 Sb.

Implementace systémů HIPS: historie a současnost. Martin Dráb

VÝUKOVÝ MATERIÁL. Bratislavská 2166, Varnsdorf, IČO: tel Číslo projektu

Základní komunikační operace

PROMĚNNÉ, KONSTANTY A DATOVÉ TYPY TEORIE DATUM VYTVOŘENÍ: KLÍČOVÁ AKTIVITA: 02 PROGRAMOVÁNÍ 2. ROČNÍK (PRG2) HODINOVÁ DOTACE: 1

Semestrální práce z předmětu Speciální číslicové systémy X31SCS

Základy informatiky. 2. Přednáška HW. Lenka Carr Motyčková. February 22, 2011 Základy informatiky 2

GPGPU Aplikace GPGPU. Obecné výpočty na grafických procesorech. Jan Vacata

PROGRAMY PRO GIS. Formovat/formulovat problém pro aplikaci v počítači. Fungování GIS programů na základní úrovni - "uvažovat" jako počítač

12 Metody snižování barevného prostoru

Matematická morfologie

Zobrazování terénu. Abstrakt. 1. Úvod. 2. Vykreslování terénu

Úvod do programovacích jazyků (Java)

Gymnázium Vysoké Mýto nám. Vaňorného 163, Vysoké Mýto

PŘETĚŽOVÁNÍ OPERÁTORŮ

Dynamicky vázané metody. Pozdní vazba, virtuální metody

Obsah. Kapitola 1 Hardware, procesory a vlákna Prohlídka útrob počítače...20 Motivace pro vícejádrové procesory...21

2.8 Procesory. Střední průmyslová škola strojnická Vsetín. Ing. Martin Baričák. Název šablony Název DUMu. Předmět Druh učebního materiálu

Základní datové struktury

Technické vývojové prostředky

Programování v jazyce C pro chemiky (C2160) 3. Příkaz switch, příkaz cyklu for, operátory ++ a --, pole

Geekovo Minimum. Počítačové Grafiky. Nadpis 1 Nadpis 2 Nadpis 3. Božetěchova 2, Brno

Pascal. Katedra aplikované kybernetiky. Ing. Miroslav Vavroušek. Verze 7

Maturitní otázky z předmětu PROGRAMOVÁNÍ

Operační systémy 2: Zápočtové úkoly

6 Příkazy řízení toku

Pokročilé architektury počítačů

Neuronové časové řady (ANN-TS)

Vektorové grafické formáty

DUM č. 14 v sadě. 31. Inf-7 Technické vybavení počítačů

Vyučovací hodina. 1vyučovací hodina: 2vyučovací hodiny: Opakování z minulé hodiny. Procvičení nové látky

Číselné soustavy v mikroprocesorové technice Mikroprocesorová technika a embedded systémy

Úvod do jazyka C. Ing. Jan Fikejz (KST, FEI) Fakulta elektrotechniky a informatiky Katedra softwarových technologií

Transkript:

MASARYKOVA UNIVERZITA FAKULTA INFORMATIKY Ð Û Å«Æ ±²³ µ ¹º»¼½¾ Ý Srovnání výpočetního výkonu programovatelných GPU a CPU (grafických procesorů a univerzálních procesorů) BAKALÁŘSKÁ PRÁCE Jiří Šimáček Brno, 2006

Prohlášení Prohlašuji, že tato bakalářská práce je mým původním autorským dílem, které jsem vypracoval samostatně. Všechny zdroje, prameny a literaturu, které jsem při vypracování používal nebo z nich čerpal, v práci řádně cituji s uvedením úplného odkazu na příslušný zdroj. Vedoucí práce: Mgr. Martin Kuba ii

Poděkování Tímto bych chtěl poděkovat vedoucímu této práce Mgr. Martinu Kubovi za vynikající podporu při řešení nesčetných problémů souvisejících s vypracováním tohoto tématu. iii

Shrnutí Tato práce se zabývá využitím výpočetního potenciálu grafického procesoru (GPU) pro účely obecných výpočetních úloh. Stručně jsou rozebrány některé techniky a přístupy, které lze aplikovat při analýze problémů, stejně jako typické situace, ve kterých je vhodné obecné výpočty na GPU provádět. Další část práce se zabývá dostupnými prostředky pro programování grafických karet, podrobněji je rozebrán nástroj BrookGPU. Na závěr jsou uvedeny výsledky srovnání výkonu univerzálního a grafického procesoru při provádění výpočtů. iv

Klíčová slova gpu, výkon, BrookGPU, paralelizace, migrace výpočtů v

Obsah 1 Úvod................................... 2 2 Základní principy zobrazování třírozměrných scén........ 3 3 Potenciální oblasti využití GPU................... 7 3.1 Techniky výpočtů......................... 8 3.2 Datové struktury......................... 10 3.3 Aplikace.............................. 10 4 Profily programovatelných jednotek................ 12 4.1 Instrukční sady.......................... 13 4.2 Příklad kódu............................ 13 5 Platformy pro komunikaci s GPU.................. 16 5.1 Nízkoúrovňová komunikace................... 16 5.2 Vyšší programovací jazyky.................... 18 5.3 Transparentní komunikace.................... 20 5.4 Srovnání nástrojů......................... 22 6 BrookGPU................................ 23 6.1 Jazyk................................ 23 6.2 Architektura............................ 25 6.3 Příklad - výpočet fraktálu.................... 26 7 Testování výkonu............................ 28 7.1 Použitý hardware......................... 28 7.2 Přesnost výpočtu......................... 28 7.3 Formát dat............................. 30 7.4 Velikost dat............................ 31 7.5 Složitost výpočtu......................... 34 7.6 Testy jednoduchých operací................... 34 8 Závěr................................... 37 8.1 Možnost využití GPU na operačním systému Linux..... 37 Literatura................................... 38 1

Kapitola 1 Úvod Herní průmysl je dnes z hlediska objemu peněz, které se v něm točí, velmi perspektivní oblastí. Na trhu panuje silná konkurence, softwarové společnosti se předhánějí v kvalitě vydávaných titulů. Jedním z hlavních aspektů těchto aplikací je grafický vzhled programu, protože ten hraje často hlavní roli při prvním dojmu ze hry. Návrháři her ruku v ruce s výrobci hardwaru tedy posouvají lat ku v tomto odvěví neustále výš, a to ve stále kratších časových intervalech. Nárůst poptávky zapříčinil vznik technologií, které i přesto, že jsou GPU (graphics processing unit grafický procesor) primárně určeny pro výpočet vizuálních scén, umožňují tyto procesory využít k náročným matematickým výpočtům. Vzhledem k povaze úloh, pro které jsou určeny, se při výpočtu hojně využívá paralelizace, což dává v součtu hrubé síly několikanásobně vyšší výkon než u dnes běžných univerzálních procesorů. Oblastí univerzálních výpočtů na grafickém hardware se v současnosti zabývá skupina GPGPU - General-Purpose computation on GPUs, jejíž domovské stránky jsou na http://www.gpgpu.org/ [16. května 2006]. Cílem této práce bylo zjistit, jaké jsou možnosti využití současného grafického hardware při provádění obecných výpočtů, vymezit potenciální oblasti uplatnění a porovnat výkon s variantami, které běží na CPU. Současně byly také prozkoumány možnosti nástrojů pro komunikaci s grafickými procesory (Cg, Sh, BrookGPU). Úvodní kapitola obsahuje historický podtext a obecné nahlédnutí do procesu vizualizace třírozměrných scén. V druhé kapitole je shrnuto několik poznatků o tom, v jakých oblastech lze výpočetní síly GPU využít a jaké techniky se při výpočtech používají. Další kapitola rozebírá jednotlivé třídy programovatelného hardware. Následující dvě kapitoly probírají některé nástroje, které je možno při programování GPU použít, zejména kapitola BrookGPU obsahuje podrobnější popis (tento nástroj byl použit při provádění testů). Předposlední kapitola je věnována výsledkům, které byly naměřeny při testování výkonu a jsou zhodnoceny v závěru této práce. 2

Kapitola 2 Základní principy zobrazování třírozměrných scén Na počátku 90. let 20.století vykonával v PC všechny výpočty související s během programu univerzální procesor. Z tohoto důvodu byla grafická úroveň tehdejších aplikací značně limitována jeho výkonem. Navíc se logika aplikací (zejména počítačových her) stávala náročnější a nebylo tedy možné spotřebovat všechen čas procesoru na vykreslování scény. V roce 1994 se na trhu objevila první přídavná karta, která částečně přebírala zobrazování třírozměrných modelů - 3Dfx Voodoo Graphics. Tento model měl pevně naprogramovanou sekvenci kroků, které se při vykreslování prováděly. Úlohou programátora tedy bylo vytvořit třírozměrný model a nahrát ho do přídavné karty. Od této chvíle začal výkon grafických karet stoupat a tak se začala do popředí dostávat otázka kvality obrazu. V praxi v té době existovaly pro tvorbu animací systémy, které sice nepracovaly v reálném čase, zato byla kvalita produkovaných scén mnohem vyšší. Jedním z nich byl například program RenderMan od firmy Pixar Animation Studios, který umožňoval provádět na jednotlivých částech scény uživatelem definované operace a tím dosahoval pozoruhodných výsledků. Tohoto nápadu se ujali výrobci grafických procesorů a začali do pevné vykreslovací sekvence přidávat programovatelné jednotky (pixel shaders a vertex shaders). Jedním z prvních grafických procesorů, který umožnoval provádět programovatelné zobrazování, byl Radeon 9700 (R300). Většina dnešních zobrazovacích systémů je založena na vykreslování dvourozměrné matice bodů. Vzhledem k tomuto faktu je úkolem grafického procesoru v případě konvenčního využití převést třírozměrnou scénu prostoru popsanou vektorově do dvourozměrného rastrového obrázku. Pro tento proces existuje několik metod (například raytracing nebo radiosita), nicméně v praxi se při zobrazování v reálném čase z výkonnostních důvodů využívá nejčastěji rasterizace. Tato metoda je velmi rychlá, avšak narozdíl od ostatních variant není založena na fyzikálním modelu světla, což je příčinou toho, že obecně produkuje nereálné výsledky. Transformace založené na rasterizaci lze rozdělit do několika fází: transformace modelu, osvětlení, 3

2. ZÁKLADNÍ PRINCIPY ZOBRAZOVÁNÍ TŘÍROZMĚRNÝCH SCÉN transformace pohledu, projekce, ořezání, rasterizace, texturování a stínování a zobrazení. Transformace modelu Na vstupu této fáze jsou třírozměrné geometrické objekty umístěné programátorem do abstraktního prostoru. Tyto objekty jsou tvořeny sítí hran a vrcholů, která je popsána množinou bodů se souřadnicemi a informací o tom, které body jsou spojeny. Při vytváření modelu lze navíc využít skládání geometrických primitiv a lokální transformace jako například posuny a rotace. Osvětlení V abstraktním prostoru jsou kromě sít ových modelů umístěny na definovaných pozicích také zdroje světla, které vytváří osvětlení scény. Toto osvětlení se skládá ze světla, které dopadá přímo, případně také z odrazů od ostatních objektů nebo některých speciálních efektů způsobených vlastnostmi povrchu. Současné grafické procesory počítají osvětlení pouze na jednotlivých hranách mnohoúhelníků, které mají být vykresleny. Ve fázi rasterizace jsou hodnoty osvětlení pro jednotlivé body vypočítány pomocí interpolace hodnot na hranách. U moderních GPU je navíc možné vypočítat osvětlení pro jednotlivé body pomocí programovatelných jednotek (pixel shaders). Transformace pohledu V abstraktním prostoru je také stanovena pozice pozorovatele a směr jeho pohledu. V této fázi dochází k transformaci objektů mezi souřadným systémem scény na souřadný systém založeným na pohledu, který má být zobrazen. Projekce Při projekci jsou objekty v třírozměrném prostoru promítnuty do dvourozměrné plochy tak, jak budou vypadat ve výsledném zobrazení. Ořezání Vzhledem k faktu, že zobrazovací zařízení má konečnou plochu, část grafických primitiv se může nacházet v prostoru, který nespadá do zobrazovaného výřezu. Tato primitiva jsou ve fázi ořezání odstraněna. Ořezání není 4

2. ZÁKLADNÍ PRINCIPY ZOBRAZOVÁNÍ TŘÍROZMĚRNÝCH SCÉN nevyhnutelně nutné k tomu, aby byla scéna zobrazena korektně, nicméně jeho použití značně urychluje další kroky výpočtu, protože eliminuje nepotřebnou rasterizaci a další zpracování obrazu v místech, která nejsou viditelná. Rasterizace Rasterizace je proces, při kterém je dvourozměrná vektorová reprezentace scény převedena do dvourozměrné mřížky odpovídající formátu výstupního zařízení. Texturování a stínování V tomto stádiu je k jednotlivým částem scény přiřazena barva na základě hodnot interpolovaných z hran objektů při rasterizaci nebo na základě textury v paměti (textura je obrázek - obvykle bitová mapa - určený k nanesení na prostorový model, který určuje výslednou podobu povrchu). Zobrazení Výsledná mřížka složená z barevných bodů může být zobrazena na výstupním zařízení. Implementace zobrazovacího procesu V praxi nejprve grafický procesor načte z paměti posloupnost hran. Tyto hrany pak podstupují proces transformace a hranového osvětlení. V tomto bodě může být u programovatelného GPU vložen uživatelský program pro zpracování hran (vertex shader), který má možnost manipulovat s daty před tím, než dojde k rasterizaci. Po dokončení transformace a osvětlení dojde k ořezání neviditelných fragmentů a zbylé části jsou převedeny do rastru. Na výsledek lze aplikovat program pro zpracování bodů (pixel shader), který upravuje vzhled jednotlivých částí a vytváří výslednou podobu scény, která je dále zobrazena. Předchozí posloupnost kroků je vhodným modelem pro hardwarové zpracování. Jednotlivé hrany a fragmenty scény mohou být zpracovávány nezávisle, což umožnuje grafickému procesoru pracovat v proudovém režimu a jednotlivé fáze procesu mohou probíhat paralelně. Zatímco tedy některé části scény jsou transformovány do rastru, jiné se nacházejí například ve fázi osvětlování. Navíc lze znásobením počtu výpočetních jednotek zajistit, že se v příslušných fázích může zpracovávat více hran a fragmentů 5

2. ZÁKLADNÍ PRINCIPY ZOBRAZOVÁNÍ TŘÍROZMĚRNÝCH SCÉN současně, a tedy dosáhnout vyššího výkonu. Na obrázku 2.1 je zobrazeno jednoduché schéma architektury grafických procesorů. Sít ový model Zpracování hran (vertex shaders) Transformace pohledu Ořezání Rasterizace Zpracování bodů (pixel shaders) Zobrazení Obrázek 2.1: Schéma architektury GPU 6

Kapitola 3 Potenciální oblasti využití GPU Výpočetní síla grafických procesorů je výsledkem úzce specializované architektury, která je již poměrně dlouhou dobu vyvíjena za účelem dosažení maximálního výkonu u silně paralelních úloh při vykreslování počítačové grafiky. Roustoucí flexibilita výpočetních jednotek grafických procesorů společně s rostoucím zájmem vývojářů o obecné výpočty prováděné na grafických kartách dnes umožňuje využít GPU i pro aplikace, které spadají mimo vyhraněnou oblast. Stále však existuje mnoho aplikací, které zatím nejsou (a možná nikdy nebudou) vhodné k vykonávání na GPU. Zpracování a sazba textu je typickým příkladem aplikace, ve které dominují nehomogení pamět ové operace a která je obtížně paralelizovatelná. Dnešní grafické procesory navíc postrádají některá základní programovací paradigmata, jako například operace s celými čísly. Související nepřítomnost operací bitových posunů a logických operací činí tyto výpočetní jednotky nepoužitelné v oblasti některých intenzivních výpočtů, jakou je třeba kryptografie. Některé další úlohy vyžadující vysokou přenost jsou nyní také obtížně aplikovatelné, protože podpora čísel s plovoucí řádovou čárkou o šířce 64 bitů nebude pravděpodobně v dohledné době v GPU implementována. Existuje však několik překážek i pro problémy, které lze na grafických procesorech řešit efektivně. Navzdory programovatelnosti a možnosti využít vyšší programovací jazyky zůstává tento výpočetní prostředek obtížně použitelný pro nevizuální úlohy. V tomto případě je totiž použit neobvyklý programovací model a programování tedy není pouze záležitostí nastudování nového jazyka nebo napsání nového generátoru kódu překladače. Algoritmy je nutné přeformulovat do terminologie zobrazování vizuálních dat, což vyžaduje programátora zběhlého ve vývoji grafických aplikací, návrhu příslušného hardwaru a jeho omezení. Tyto překážky jsou způsobeny pouze specializací GPU na vykreslování třírozměrných scén nikoliv však chybným návrhem grafických karet. Nelze tedy pouze vyčkávat na příchod jedné nebo dvou dalších generací, které přinesou vyšší přesnost a bohatší instrukční sadu. Využití grafického procesoru pro účely obecných výpočtů 7

3. POTENCIÁLNÍ OBLASTI VYUŽITÍ GPU vyžaduje znalosti jednak v oboru počítačové grafiky a současně v příslušné vědecké nebo praktické oblasti. Vzhledem k faktu, že dnešní grafické procesory vykazují pokročilou architekturu a jejich výkon roste mnohem rychleji než u procesorů univerzálních, není možno i přes všechny překážky v programovacím modelu jejich potenciální přínos přehlédnout. Tato kapitola dále rozebírá různé oblasti využití grafických procesorů v praktických aplikacích. Informace jsou čerpány převážně z [4] a [7], kde je problematika rozebrána podrobněji. 3.1 Techniky výpočtů Z abstraktního pohledu je model programování GPU podobný práci s proudem nezávislých dat. Existuje několik základních operací, které mohou být s proudy dat prováděny a které používá mnoho aplikací pracujících s grafickými procesory. Mezi ně patří mapování, redukce, nepřímé čtení a nepřímý zápis, filtrování, řazení a vyhledávání. Mapování Jednou z nejednodušších technik využitelných při práci s proudy dat je mapování. Princip spočívá v aplikaci dané funkce na jednotlivé elementy vstupu. Implementace pomocí GPU je přímočará, vstupní data se nahrají do paměti jako textura a funkce se naprogramuje do GPU, které ji v průběhu generování scény aplikuje na každý element textury. Redukce Při výpočtu může dojít k situaci, kdy je třeba ze vstupního proudu vytvořit výstupní proud, který obsahuje méně, případně pouze jeden prvek. Jedná se tedy o redukci. Na grafických procesorech lze redukci provádět pomocí střídavého zpracovávání dvou textur, přitom v každém průchodu se velikost výstupu sníží na polovinu. Obecně lze redukci vypočítat v logaritmickém počtu kroků na paralelním GPU oproti lineárnímu počtu kroků na CPU. Nepřímé čtení a nepřímý zápis Při transformaci textur provádí GPU výpočet postupně se všemi prvky. Při každém kroku se určí adresa bodu (pixelu), který bude zpracován a zároveň se vypočítá místo, kde bude uložen výsledek. Programy mohou přebírat obecně více vstupů, ale mohou produkovat pouze jednu výstupní hodnotu. 8

3. POTENCIÁLNÍ OBLASTI VYUŽITÍ GPU Výpočetní jednotky jsou vybaveny instrukcemi, které umožňují číst data z libovolné pozice v paměti (textury) nepřímé čtení (gather). Fakt, že je adresa výstupu určena před spuštěním programu, znemožnuje přímočaře provádět nepřímý zápis (scatter). Z tohoto důvodu byly vyvinuty některé metody, které umožnují operaci nepřímého zápisu obejí je například možné výsledek nejprve uložit na stanovenou adresu a později data seřadit. Filtrování V případě filtrování se jedná o výběr některých prvků z datového proudu podle zadaného kritéria. V terminologii GPU jde v podstatě o speciální případ redukce, nicméně vzhledem k faktu, že adresa ani počet prvků není znám předem, nelze tyto operace provádět stejným způsobem. Filtrování lze dosáhnout kombinací několika různých metod s vícenásobnými průchody dat. Řazení Řazení je operace která přeskládá prvky v datovém proudu tak, aby jejich pořadí vyhovovalo dané relaci uspořádání. Pro řazení pomocí univerzálních procesorů bylo navrženo několik technik, které však většinou u GPU nelze použít, protože jejich kroky jsou datově závislé a obecně vyžadují operaci rozptýlení. Většina implementací pro grafické procesory je založena na řadících sítích. Základní myšlenkou u tohoto přístupu je princip řazení založený na pevně stanoveném počtu kroků, který je nezávislý na vstupních datech. Navíc mají všechny uzly v řadící síti pevné komunikační cesty, problém lze tedy formulovat pomocí operaci shromáždění namísto rozptýlení a stanovený počet fází pro daný vstup umožňuje eliminovat datově závislé větvení. Celkově je tak možné docílit uspořádání proudu v Ç(Òlog 2 (Ò)) krocích. U řazení byly také zkoumány metody, které využívají texturovací fáze procesu vykreslování scény a plně eliminují potřebu programovatelných jednotek. Vyhledávání Při vyhledávání je cílem nalézt jistý prvek v proudu dat, případně množinu prvků, které jsou hledanému prvku nejblíže (například databázové dotazy). U implementací pro GPU je efektivní provádět několik vyhledávání současně, a tím zvýšit propustnost. Základní variantou je binární vyhledávání, které pracuje nad seřazeným seznamem a v každém kroku porovnává prostřední prvek s prvkem, který 9

3. POTENCIÁLNÍ OBLASTI VYUŽITÍ GPU je hledán. Na základě výsledku aplikuje stejný postup na levou nebo pravou polovinu dat, dokud nenajde příslušný prvek nebo neurčí, že takový prvek neexistuje. Algoritmus binární vyhledávání je vnitřně sekvenční, nelze tedy paralelizovat vyhledávání jediného prvku. Pomocí grafického procesoru však lze jednoduše provádět současně vyhledávání více prvků ve stejných datech. 3.2 Datové struktury Každý algoritmus pracuje s daty, která jsou uložena v nějaké datové struktuře. Datové struktury, které se využívají při výpočtech na GPU musí podporovat rychlý a bezpečných přístup (z hlediska synchronizace) a současně efektivní paralelní iteraci. Zároveň je žádoucí aby činnost datové struktury respektovala vnitřní pamět ový model GPU a proces tak bylo možné provádět efektivně. Hodnoty jsou v tomto případě v paměti uloženy téměř vždy v podobě textury, která má dva rozměry. Jednorozměrná data by mohla být uložena jako jeden řádek, nicméně maximální velikost textury v jednom rozměru bývá omezena, proto se obvykle používá mapování z jednodimenzionálního do dvoudimenzionálního prostoru. 3.3 Aplikace Diferenciální rovnice Diferenciální rovnice se používají v mnoha oblastech výzkumu a inženýrství. Jejich efektivní řešení je zvláště nezbytné pro simulování fyzikálních jevů. V typickém případě se obvykle řeší výpočet pro velkou sadu dat, tento problém je tedy přirozeně paralelizovatelný a lze jej pohodlně řešit pomocí GPU. Lineární algebra Operace s vektory a maticemi mohou být opět dobře decomponovány na nezávislé paralelní úlohy. Bylo ukázáno, že operace jako násobení matic lze pomocí GPU provádět rychleji než při tradičním přístupu. Při výzkumu v této oblasti byl odhalen fakt, že jsou tyto operace značně omezeny pamět ovou propustností a byly navrženy některé změny v návrhu grafických procesorů, které by tyto problémy redukovaly a posunuly tak výkon v těchto výpočtech na vyšší úroveň. 10

3. POTENCIÁLNÍ OBLASTI VYUŽITÍ GPU Ostatní oblasti využití Výpočetního výkonu GPU lze využít i v mnoha dalších oblastech jako jsou třeba databázové systémy, u kterých lze snadno implementovat výpočet agregačních funkcí typu průměrná hodnota, maximum, apod., případně využít možnosti souběžného vyhledávání. Vhodně lze paralelizovat také úlohy, které provádějí zpracování signálu, dále také různé metody výpočtu globálního osvětlení, mezi které patří raytracing, fotonové mapy, radiosita nebo subsurface scattering, či geometrické výpočty. 11

Kapitola 4 Profily programovatelných jednotek S vývojem výpočetních jednotek se také vyvíjely nároky, které byly na tyto jednotky kladeny. Na počátku bylo umožněno používat pouze velmi krátké programy, navíc bez podmíněného větvení, které měly k dispozici pouze několik málo registrů. Schopnosti čipů se rozšiřovaly, zároveň se však odlišovaly mezi jednotlivými výrobci. Aby mohly být tyto výpočetní prostředky využity v praxi, vznikla potřeba jisté klasifikace. Za tímto účelem byly vytvořeny třídy (shader model SM), které specifikují, jaké minimální požadavky musí čip splňovat. V tabulce 4.1 jsou uvedeny příklady vlastností a hodnot pro třídy SM 2.0 a 3.0 (jednotky pro zpracování pixelů). Informace v této kapitole byly z části čerpány z vývojové sady DirectX SDK[5]. vlastnost SM 2.0 SM 3.0 max. délka programu 96 512 max. počet vykonaných instrukcí 96 65535 max. počet texturovacích instrukcí 32 bez omezení poziční registr ne ano počet interpolačních registrů 10 10 predikce instrukcí ne ano registry pro indexování vstupu ne ano dočasné registry 12 32 registry konstant 32 224 přechodové instrukce ne ano čítač cyklu ne ano dynamická kontrola toku ne 24 Tabulka 4.1: Srovnání tříd Shader Model 2.0 a Shader Model 3.0 Z tabulky 4.1 je patrné, že programovatelnost jednotek je značně závislá na implementovaném modelu. Navíc zde nejsou uvedena některá další omezení, jako například fakt, že doposud není možné vytvořit počítaný cyklus s počtem opakování přesahujícím 255 iterací. Podobná omezení budou pravděpodobně odstraněna v některém z dalších modelů v součas- 12

4. PROFILY PROGRAMOVATELNÝCH JEDNOTEK nosti je ve vývoji Shader Model 4.0, jehož uvedení bude doprovázet vydání nové verze aplikačního rozhraní DirectX pro systémy Microsoft Windows. Dnešní grafické procesory podporují nejčastěji Shader Model 3.0. V tabulce 4.2 jsou uvedeny příklady GPU a příslušné verze hardwarových profilů. čip profil Radeon 9800 (R350) SM 2.0 Radeon X800 (R420) SM 2.0 Radeon X1800 (R520) SM 3.0 GeForce FX (NV30) SM 2.0 GeForce 6 (NV40) SM 3.0 GeForce 7 (G70) SM 3.0 Tabulka 4.2: Podpora hardwarových profilů 4.1 Instrukční sady Vzhledem k tomu, že grafické procesory jsou určeny ke zcela odlišnému účelu, než procesory univerzální, vypadají také instrukční sady odlišně. U univerzálních procesorů se vyskytuje sada instrukcí pro aritmetické operace s celými čísly a čísly s plovoucí řádovou čárkou, instrukce pro bitové posuny a rotace, instrukce pro logické operace a instrukce pro řízení toku programu. Navíc dnešní procesory obsahují instrukce MMX a SSE, které umožňují vektorové a maticové operace. Naproti tomu GPU zatím obsahují pouze aritmetické instrukce pro práci s čísly s plovoucí řádovou čárkou, a to jak se skalárními hodnotami, tak s vektorovými i maticovými daty. Shader Model 3.0 přidává instrukce pro podmíněné větvení programu. Naopak chybí například celočíselné instrukce a instrukce pro logické operace, ty však budou pravděpodobně uvedeny v nadcházející třídě Shader Model 4.0. V tabulce 4.3 jsou uvedeny některé instrukce grafických procesorů. 4.2 Příklad kódu Překladač BrookGPU vygeneruje z programu kernel void multiply( float4 a<>, out float f<> ) { f = a.x * a.y + a.z * a.w; } následující kód: 13

ps_3_0 def c0, 1, 0, 0, 0 dcl_texcoord v0.xy dcl_2d s0 mul r0, v0.xyxx, c0.xxyy texldl r0, r0, s0 dp2add oc0.x, r0.xzzw, r0.ywzw, c0.y mov oc0.yzw, c0.y 4. PROFILY PROGRAMOVATELNÝCH JEDNOTEK Prvním příkazem je pseudoinstrukce, která určuje verzi hardwarového profilu, pro který je kód napsán. Instrukce def c0, 1, 0, 0, 0 definuje konstantu c0, která má 4 složky 1, 0, 0 a 0. dcl_texcoord v0.xy nadeklaruje registr se souřadnicemi v textuře (jeho obsah je stanoven v předchozích fázích výpočtu). dcl_2d s0 nadeklaruje vstupní registr programu v tomto případě vzorkovací registr textury. Instrukce ps, def a dcl* musí předcházet všechny funkční instrukce, jinak by program nebylo možné provést. mul r0, v0.xyxx, c0.xxyy provede vynásobení registru v0 a konstanty c0, výsledek uloží do r0. Notace v0.xyxx způsobí, že se operand v0 chová, jako by jeho složky x, y, z, w obsahovaly postupně hodnoty složek x, y, x a x. Hodnota těchto složek v původním operandu zůstane nezměněna. Instrukce texldl r0, r0, s0 načte z textury (reprezentované vzorkovacím registrem s0) hodnotu bodu na souřadnocích uložených v registru r0 a uloží ji do r0. dp2add oc0.x, r0.xzzw, r0.ywzw, c0.y provede skalární součin na prvních dvou složkách operandů r0.xzzw a r0.ywzw, k výsledku přičte c0.y (v tomto případě 0) a uloží do výstupního registru oc0. mov oc0.yzw, c0.y nakonec vynuluje ostatní složky registru oc0. 14

4. PROFILY PROGRAMOVATELNÝCH JEDNOTEK symbolický zápis uvedeno v modelu popis nop SM 1.0 žádná operace ps SM 1.0 pseudoinstrukce určující hw. profil def SM 1.0 definice konstanty mov SM 1.0 přesun hodnoty add SM 1.0 součet mul SM 1.0 součin lrp SM 1.0 lineární interpolace dp4 SM 1.0 4-složkový skalární součin tex SM 1.0 navzorkování textury dcl SM 2.0 deklarace vstupního registru dp2add SM 2.0 2-složkový skalární součin a přičtení abs SM 2.0 absolutní hodnota min SM 2.0 minimum sincos SM 2.0 sinus a kosinus texldl SM 3.0 navzorkování textury s úrovní detailu call SM 3.0 volání podprogramu crs SM 3.0 vektorový součin label SM 3.0 návěští loop SM 3.0 začátek cyklu Tabulka 4.3: Některé instrukce grafických procesorů 15

Kapitola 5 Platformy pro komunikaci s GPU V současnosti existuje několik platforem, které umožnují programátorovi komunikaci s GPU na různých úrovních abstrakce. V této kapitole jsou stručně popsány některé z rozšířených nástrojů. Tyto obvykle komunikují s hardwarem pomocí aplikačního rozhraní DirectX nebo OpenGL, některé z nich umožnují rozhraní vybrat. Podle abstrakce, na které se uživatel při práci pohybuje, lze interakci s grafickými výpočetními prostředky rozdělit na nízkoúrovňovou, na úrovni vyšších programovacích jazyků a transparentní. 5.1 Nízkoúrovňová komunikace S využitím rozhraní OpenGL nebo DirectX lze do programovatelných jednotek nahrávat přímo strojový kód (symbolické instrukce). Tento přístup má však podobné nevýhody jako programování CPU v asembleru. Vyvíjet programy tímto způsobem je tedy časově náročné a výsledný kód je závislý na konkrétní instrukční sadě. DirectX DirectX je aplikační rozhraní běžící na operačních systémech Microsoft Windows vyvinuté firmou Microsoft, které umožňuje komunikovat s hardware počítače s minimální režií. Jeho záběr se nevztahuje pouze na oblast grafiky, nicméně ta je jeho stěžejní součástí. Rozhraní je postaveno na modelu COM, je tedy možné jej využít v libovolném vývojovém prostředí, které tento binární standard podporuje. Tato platforma je využívána většinou výrobců počítačových her. Nahrání programu do GPU může probíhat následovně: ID3DXEffect* g_peffect = NULL; D3DXCreateEffectFromFile( pd3ddevice, program.fx, NULL, 16

5. PLATFORMY PRO KOMUNIKACI S GPU NULL, 0, &g_peffect, NULL ); Po inicializaci rozhraní DirectX (viz. [5]), při kterém je vytvořen objekt zpřístupňující služby zařízení (pd3ddevice), je možné ze souboru načíst program zapsaný v symbolických instrukcích. Při zavádění je zároveň pomocí služeb ovladače grafické karty zajištěn překlad těchto instrukcí do strojového kódu konkrétního GPU. V místě programu, kde je prováděno vykreslování scény lze poté umístit následující operace: g_peffect->begin(&cpasses, 0); for (ipass = 0; ipass < cpasses; ipass++) { } g_peffect->beginpass(ipass); g_pmesh->drawsubset(0); g_peffect->endpass(); g_peffect->end(); Příkaz g_peffect->begin(&cpasses, 0) připraví program k vykonání a současně zjistí, kolik průchodů je při výpočtu nutné provést. Zavolání metody g_pmesh->drawsubset(0) provede vykreslení objektu a tím způsobí vykonání programu neexistuje žádný jiný způsob, jak donutit GPU, aby daný program spustilo. g_peffect->endpass() ukončí vykonání aktuálního průchodu a nakonec g_peffect->end() ohlásí systému, že kreslení pomocí programu skončilo. OpenGL OpenGL je aplikační rozhraní vyvinuté společností Silicon Graphics. Narozdíl od DirectX je zaměřeno pouze na komunikaci s grafickým hardware a je dostupné na mnoha odlišných operačních systémech a platformách. Tato knihovna je soubor přibližně 250 funkcí ve stylu jazyka C a lze ji využít téměř v libovolném prostředí. Díky portabilitě je OpenGL využíváno v profesionálních aplikacích, CAD systémech, při vizualizaci vědeckých výpočtů a také v počítačových hrách. V OpenGL slouží pro práci s programovatelnými jednotkami několik funkcí. Stejně jako u DirectX je nejprve nutné vhodně inicializovat scénu. Povolení rozšíření OpenGL, které umožňuje práci s programovatelnými jednotkami, zajistí volání glenable(gl_fragment_program_arb). Nyní je nutné přiřadit programu číslo, pomocí kterého bude program identifikován. To zajistí dvojice příkazů glgenprogramsarb(1, &id); glbindprogramarb(gl_fragment_program_arb, id); 17

5. PLATFORMY PRO KOMUNIKACI S GPU Proměnná id obsahuje identifikátor, ke kterému se budou vztahovat následující operace. V dalším kroku lze načíst samotný program, který je opět zapsán pomocí symbolických instrukcí. Ten je nejdříve nutné libovolným způsobem nahrát do paměti a poté předat GPU: char* buffer[65536]; FILE* fprg = fopen( program.cg, rb ); buffer[fread(buffer, 65535, 1, fprg)] = 0; glprogramstringarb( GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(buffer), buffer ); V tomto momentě knihovna společně s ovladačem grafické karty opět zajistí překlad do strojového kódu pro konkrétní hardware. V posledním kroku lze opět zakázat podporu rozšíření pro programovatelné jednotky (aby nebyly nechtěně použity při vykreslování) příkazem gldisable(gl_fragment_program_arb). Stejně jako u DirectX je program spuštěn při vykreslování některého objektu. V OpenGL zajistí aktivaci programu následující posloupnost příkazů při vykreslování scény: glenable(gl_fragment_program_arb); glbindprogramarb(gl_fragment_program_arb, id);... /* vykreslení objektů */ gldisable(gl_fragment_program_arb); 5.2 Vyšší programovací jazyky Závislost na instrukční sadě lze odstranit použitím vyššího programovacího jazyka a překladače, který generuje kód v závislosti na konkrétní platformě. Současně se tím výrazně zjednodušší a zrychlý vývoj programů. I přesto však ani tato varianta není příliš vhodná pro obecné výpočty na GPU, protože programátor musí ovládat principy používané v počítačové grafice. HLSL High Level Shading Language (HLSL) [6] je programovací jazyk určený pro vývoj programů pro GPU, jehož syntaktické konstrukce jsou podobné jazyku C. HLSL je podporován přímo rozhraním DirectX, které umožnuje překládat kód za běhu, 18

5. PLATFORMY PRO KOMUNIKACI S GPU nicméně Microsoft poskytuje také samostatný překladač fxc. Zápis v HLSL může vypadat následovně: struct v2p { float4 color: COLOR0; }; struct p2f { float4 color: COLOR0; }; void main(in v2p IN, out p2f OUT) { } float4 t = IN.color*float4(0.299, 0.587, 0.114, 0); float grey = t.x + t.y + t.z; OUT.color = float4(grey, grey, grey, IN.w); Předchozí program provádí jednoduchou činnost převod barevného obrázku na stupně šedé barvy. Struktura v2p definuje formát vstupních parametrů, které jsou vypočítány v předchozích fázích procesu. p2f definuje formát výstupních parametrů. Tělo programu je zapsáno ve funkci main. Nejdříve se ze vstupu přečte hodnota barvy zpracovávaného bodu, která se po složkách vynásobí koeficienty intenzity pro jednotlivé barvy (červená, zelená, modrá). Výsledná hodnota se dále sečte a uloží do pomocné proměnné, která poté obsahuje výslednou intenzitu. Ta se pak uloží do všech barevných složek výstupu. Typ float4 je struktura, která obsahuje 4 položky typu float x, y, z, w. V případech, kdy proměnná tohoto typu obsahuje údaje o barvě, je složka w obvykle využita pro určení průhlednosti bodu. Cg Cg [3] je zkratka výrazu C for graphics. Jedná programovací jazyk s podobnými vlastnostmi jako má HLSL. Cg je produkt společnosti nvidia a je možné jej použít pomocí rozhraní DirectX a narozdíl od HLSL také pomocí OpenGL. Programy je možné překládat v době běhu nebo je možné využít externího překladače cg. Následuje příklad jednoduchého programu: struct pixel_in { float4 color: COLOR0; }; struct pixel_out { float4 color: COLOR0; }; pixel_out main(pixel_in IN) { pixel_out OUT; float4 t = IN.color*float4(0.299, 0.587, 0.114, 0); float grey = t.x + t.y + t.z; 19

5. PLATFORMY PRO KOMUNIKACI S GPU OUT.color = float4(grey, grey, grey, IN.w); return OUT; } Předchozí kód opět provádí převod barevného obrázku na stupně šedi. Na první pohled je patrné, že se struktura programu výrazně neliší od zápisu v HLSL. Program obsahuje deklarace vstupu a výstupu (pixel_in, pixel_out). Funkce main nyní přebírá pouze vstupní parametr a výstup vrací jako svou návratovou hodnotu. 5.3 Transparentní komunikace Pro účely obecných výpočtů je výhodné použít nástroje, které se pokouší skrýt detaily nutné při komunikaci s GPU a snaží se být transparentní. V praxi to znamená, že při psaní programů pro GPU může programátor využít podobné techniky jako u programů pro CPU. Sh Pomocí Sh [8] lze psát aplikace pro grafické procesory přímo v jazyce C++. Samotný nástroj je v podstatě knihovna, která umožňuje překládat speciální konstrukce do strojového kódu pro GPU. Takto předpřipravené programy pak na vyžádání do GPU nahraje a spustí výpočet. Přestože je Sh vysoce transparentní, lze jej využít k obecným výpočtům stejně jako pro grafické aplikace. Programu napsaný pomocí Sh může vypadat takto: int main() { shinit(); ShProgram prg = SH_BEGIN_PROGRAM( gpu:stream ) { ShInputColor4f in; ShOutputColor4f out; ShAttrib4f t = in*shattrib4f(0.299, 0.587, 0.114, 0); ShAttrib1f grey = t(0) + t(1) + t(2); out = ShOutputColor4f(grey, grey, grey, in(4)); } SH_END; float data[] = { 1.0, 0.5, -0.5, 1 }; 20

5. PLATFORMY PRO KOMUNIKACI S GPU ShHostMemoryPtr mem_in = new ShHostMemory( sizeof(float)*4, data, SH_FLOAT ); ShChannel<ShColor4f> in(mem_in, 1); float outdata[4]; ShHostMemoryPtr mem_out = new ShHostMemory( sizeof(float)*4, outdata, SH_FLOAT ); ShChannel<ShColor4f> out(mem_out, 1); out = prg << in; mem_out->hoststorage()->sync(); float* results = static_cast<float*>( mem_out->hoststorage()->data() ); std::cout << out = ( << results[0] <<, << results[1] <<, << results[2] << ) << endl; return 0; } Program opět provádí převod mezi barevnými a šedými obrázky. V tomto případě je však kód pro GPU popsán speciální konstrukcí v jazyce C++, která je definována v knihovně Sh (SH_BEGIN_PROGRAM, SH_END). Struktura samotného kódu je opět velmi podobná (přístup k jednotlivým složkám typů Sh* je zajištěn operátorem ()). Na rozdíl od předchozích nástrojů však Sh umožňuje také samotné načtení a spuštění programu. Nejdříve jsou vytvořeny dvě textury (new ShHostMemory(... ), které jsou svázány s datovými kanály (ShChannel). Poté je spuštěn samotný program (out = prg << in). Následuje synchronizace, která po dokončení výpočtu zkopíruje data zpět do systémové paměti. BrookGPU BrookGPU [1, 9] je narozdíl od předchozích nástrojů určen výhradně pro obecné výpočty. Pomocí BrookGPU byly prováděny experimenty související s touto prací a podrobněji se mu věnuje následující kapitola. 21

5. PLATFORMY PRO KOMUNIKACI S GPU 5.4 Srovnání nástrojů Psaní programů v symbolických instrukcích je poměrně časově náročné a přináší řadu překážek, které je nutno při programování překonat. Na druhou stranu je to prostředek, kterým se dá dosáhnout nejlepšího výkonu provedením optimalizací, které překladač nezvládne. Při programování grafických aplikací se často využívají jazyky HLSL nebo Cg, protože umožnují psát vizuální efekty elegantním způsobem ve stylu jazyka C. Naopak pro psaní programů pro obecné výpočty je vhodnější použít Sh nebo BrookGPU. Sh představuje rozumný kompromis, který jednak obaluje většinu základních úkonů souvisejících s inicializací GPU a zároveň ponechává programátorovi jistou kontrolu nad prováděnými kroky. Naopak BrookGPU je plně transparentní, jeho cílem je osvobodit uživatele od technologického pozadí. 22

Kapitola 6 BrookGPU BrookGPU [1, 9] je nástroj, který umožňuje využívat výpočetní potenciál grafických procesorů transparentním způsobem. Obsahuje překladač jazyka Brook, což je rozšíření jazyka C přizpůsobené pro paralelní výpočty s datovými proudy. Součástí je také běhová knihovna, která zajišt uje komunikaci s grafickým hardware. Účelem tohoto volně šiřitelného balíku je kromě zprostředkování možnosti vytvářet aplikace pro GPU také demonstrace obecných výpočtů na grafických procesorech a výzkum možností programového modelu založeného na operacích s datovými proudy. 6.1 Jazyk Program se skládá z konstrukcí jazyka C, které jsou obohaceny o možnost deklarovat datové proudy a speciální funkce označované jako kernel. Datové Proudy Proud je nový datový typ, který reprezentuje sadu dat, která mohou být zpracována paralelně. Deklarace proudu je podobná deklaraci pole a vypadá následovně: float a<8, 8>; Předchozí příklad vytváří dvourozměrný datový proud a, jehož prvky jsou typu float. Celkově proud obsahuje 64 prvků a má podobu matice o rozměrech 8 na 8. Proud má řádkovou reprezentaci, deklarace typu <10> je ekvivalentní <1, 10>, případně <1, 1, 10>. Práce s datovými proudy má na rozdíl od běžných polí jistá omezení, která jsou způsobena vnitřní architekturou BrookGPU: přímý přístup (a[1][0]) je přípustný pouze uvnitř kernelů není povolena statická inicializace (float a<10> = 2.0, 2.1,... ) proudy mohou být deklarovány pouze na zásobníku (lokálně) mimo kernely lze číst z a zapisovat do proudu pouze pomocí speciálních operátorů streamread a streamwrite streamread(a, data); streamwrite(a, data); 23

6. BROOKGPU Jazyk navíc kromě standardního typu float obsahuje také typy float2, float3 a float4, které jsou definovány následovně: typedef struct { float x, y; } float2; typedef struct { float x, y, z; } float3; typedef struct { float x, y, z, w; } float4; Kernely Kernely jsou zvláštní funkce, které pracují s datovými proudy a jsou spouštěny paralelně. Zavolání kernelu na sadu vstupních proudů má stejný efekt jako aplikace těla kernelu na každý jednotlivý prvek, ovšem v tomto případě probíhají výpočty na několika prvcích současně. Pokud se operace provádí přímo na GPU, jsou datové proudy přeneseny do paměti grafického adaptéru a kernely jsou přeloženy do strojového kódu pro grafický procesor. Definice kernelů připomíná definici funkce, zápis navíc předchází klíčové slovo kernel a návratová hodnota kernelu musí být vždy void. V deklaraci musí být jeden proud označen jako výstupní klíčovým slovem out. Definice může vypadat takto: kernel void k( float s<>, float3 f, float a[10][10], out float o<> ) {... } V tomto případě bude tělo kernelu k zavoláno na každý prvek proudu s. Při vyvolání těla obsahuje proměnná s hodnotu příslušného prvku a nelze do ní zapisovat. Proměnná f je konstantním parametrem, který bude mít pro všechny prvky stejnou hodnotu, stejně jako proměnná a. Ta má však charakter dvourozměrného pole, k jehož prvkům lze přistupovat pomocí operátoru []. Jako vstup v podobě pole může být předán libovolný datový proud. Každé provedení těla kernelu produkuje jednu hodnotu ve výstupním proudu, kterou zastupuje proměnná o. Její hodnotu lze pouze zapisovat. Tělo kernelu může obsahovat pouze konstrukce, které jsou podporovány v jazycích Cg nebo HLSL. Mezi ně patří například vektory, matice nebo některé běžně používané funkce jako například sinus, kosinus, logaritmus, atd. Volání kernelu je stejné jako volání ostatních funkcí: kernel void k( float s<>, float3 f, float a[10][10], out float o<> ); float s1<100>; float s2<100>; float s3<10,10>; streamread(s1, data1); 24

6. BROOKGPU streamread(s2, data2); // spuštění kernelu k k(s1, 1.0f, s2, s3); streamwrite(s3, result); Redukce Jazyk podporuje také speciální varianty kernelů redukce. Od běžných kernelů se liší tím, že počet prvků ve výstupním proudu může být menší než v proudu vstupním. Z důvodu paralelní implementace je však vyžadováno, aby operace, kterou redukce provádí, byla komutativní a asociativní. Tomuto požadavku vyhovují například operace součet, součin, minimum/maximum, logický součet, logický součin nebo logická nonekvivalence (XOR). Operace rozdílu nebo podílu naopak nejsou komutativní ani asociativní, nicméně překladač tuto chybu nemusí odhalit a tedy chybně definované redukce povedou k nesprávným výsledkům. Následuje příklad sečtení všech prvků proudu: void reduce sum ( float a<>, reduce float result<> ) { result = result + a; } Redukce přebírá vždy pouze jeden vstupní a jeden výstupní proud stejného typu. Ostatní argumenty mohou být pouze konstantní. Redukce má narozdíl od kernelu možnost z výstupního parametru číst hodnotu. Pokud je jako výstupní parametr předána skalární proměnná, pak je výsledek redukce jedna hodnota. V případě proudu se překladač pokusí určit redukční poměry a pokud se mu to nepodaří, tak ohlásí chybu. Například pro vstupní proud o rozměrech (100, 200) a výstupní proud o velikosti (50, 20) bude po provedení v každém prvku uložen výsledek redukce obdélníkové části vstupu o rozměrech 2 a 10. Více informací o tomto programovacím nástroji společně s kompletní dokumentací lze nalézt v [9]. 6.2 Architektura BrookGPU obsahuje dvě hlavní součásti. Překladač BRCC, který zpracovává zdrojové soubory v jazyce BrookGPU (.br) do zdrojových souborů jazyka C++ (.cpp). Výsledné soubory lze přeložit společně s běhovou knihovnou BrookGPU, což je druhá součást tohoto nástroje, která zajišt uje komunikaci s grafickou kartou. Běhová knihovna obsahuje programovou vrstvu nezávislou na konkrétní architektuře, která implementuje podporu pro základní konstrukce Brooku pro příslušný hardware. Součástí je knihovna tříd, která poskytuje jednotné rozhraní, které je používáno překladačem. Implementace funkcí určených pro GPU jsou vytvořeny v různých verzích (pro různé profily hardwarových jednotek a pro CPU). 25

6. BROOKGPU BrookGPU obsahuje podporu pro platformy DirectX9, OpenGL, NVIDIA NV3x a referenční implementaci v C++, která běží na CPU. Pomocí proměnné prostředí BRT RUNTIME, která může obsahovat hodnoty dx9, ogl, nv30gl nebo cpu, lze vybrat rozhraní, které se má použít při komunikaci s GPU (kromě volby cpu). 6.3 Příklad - výpočet fraktálu Příkladem aplikace, která je přirozeně paralelizovatelná, je například výpočet fraktálu. Jedním ze základních fraktálů je hranice Mandelbrotovy množiny, což je množina bodů v komplexní rovině, pro něž opakované provedení přiřazení Þ Þ 2 + (Þ ¾C) nevede k nekonečnému Þ (obrázek 6.1). Obrázek 6.1: Mandelbrotova množina (černě) V praxi se obvykle výpočet aproximuje tak, že za nekonečno se považuje moment, kdy Þ 2. Pro určení příslušnosti do této množiny je nutné zkoumat každý bod roviny zvlášt, a protože při výpočtu nejsou použity žádné další informace, lze tedy výpočet provádět na všech bodech roviny současně. Program vytvořený pomocí BroogGPU, který tento fraktál počítá, vypadá následovně: 26

kernel void fractal(float2 plane<>, out float f<>) { } float i, x = 0, y = 0, t; for (i = 1; i < 255 && ((x*x + y*y) < 4.); ++i) { t = x*x - y*y + plane.x; y = 2*x*y + plane.y; x = t; } f = ((x*x + y*y) < 4.)?(0):(i); 6. BROOKGPU Proměnná plane obsahuje souřadnice jednotlivých bodů v rovině (). Pro každý bod se několikrát zopakuje výpočet Þ Þ 2 + a průběžně se zkoumá velikost absolutní hodnoty. Výstupem programu je počet opakování, který je nutný provést, aby absolutní hodnota přesáhla číslo 2, nebo 0, pokud absolutní hodnota této hranice nedosáhla. Tento výsledek se pak použije při vizualizaci (body, jejichž absolutní hodnota nepřesáhla2, jsou zobrazeny černě). 27

Kapitola 7 Testování výkonu První část této kapitoly je zaměřena na přesnost výpočtů prováděných na GPU, dále jsou uvedeny výsledky dosažené při měření rychlosti výpočtů. Program pro testování výkonu byl vytvořen pomocí nástroje BrookGPU. Testy byly zaměřeny na různé aspekty výpočtu od velikosti dat přes komplexnost operací až po vnitřní formát parametrů. Jednotlivé případy byly testovány na GPU i na CPU (v tomto případě byla použita optimalizovaná verze, protože referenční implementace generovaná BrookGPU vykazuje nižší výkon a je určena spíše pro ladicí účely), aby bylo možné porovnat přínos migrace výpočtu na grafický procesor. 7.1 Použitý hardware Pro účely této práce byla pořízena grafická karta ze série ATI Radeon X1900 s pamětí o velikosti 512 MB vybavená čipem s označením R580, který má následující vlastnosti: 384 milionů tranzistorů, výrobní proces 90nm 48 procesorů pro zpracovaní pixelů (pixel shaders) 8 procesorů pro zpracování vektorových dat (vertext shaders) 256-bitové osmikanálové pamět ové rozhraní nativní podpora sběrnice PCI Express x16 Karta byla osazena do počítače s procesorem AMD Athlon 64 3700+, který disponuje pamětí 512 MB. Na tomto stroji byly prováděny všechny související experimenty (pod operačním systémem Microsoft Windows XP). 7.2 Přesnost výpočtu Množina reálných čísel je nekonečná, dnešní počítače však mají pouze konečnou pamět. Z tohoto důvodu je nutné pro práci s reálnými čísly používat konečnou aproximaci. Vhodnou variantou jsou čísla s plovoucí řádovou čárkou (floatingpoint numbers). Ty mají následující tvar: mantisa 2 exponent 28

7. TESTOVÁNÍ VÝKONU První bit uchovává informaci o znaménku čísla, následuje mantisa a exponent, který udává, o kolik řádů je desetinná čárka posunuta (vlevo nebo vpravo). Vzhledem k tomu, že je tato reprezentace konečná, je také konečná množina čísel, které lze v tomto formátu uchovat. Z toho plyne, že výsledek operace (součet, násobení, atd.) nemusí mít vždy reprezentaci v rámci této množiny. V takovém případě dochází k zaokrouhlení na některou z blízkých hodnot. Příkladem může být následující výpočet, který provádí součet dvou čísel s mantisou o šířce 4 bity: (16+1 5) 10 =(1 000 2 4 +1 100 2 0 ) 2 =(1 0011 2 4 ) 2 =17 5 10 Výsledek nelze zapsat do 4 bitů, je tedy nutné použít zaokrouhlení. Nejbližší hodnoty, které lze ve vymezeném prostoru rerezentovat jsou(17) 10 =(1 001 2 4 ) 2 a(18) 10 =(1 010 2 4 ) 2. Norma IEEE 754 1985 říká, že se zaokrouhluje vždy na nejbližší hodnotu, v případě shody na hodnotu, která je sudá. Zaokrouhlovací chyba, která takto vznikne, je tedy nejvýše jedna polovina hodnoty jednotky na posledním místě (ULP 1 ). Návrháři univerzálních procesorů se touto normou řídí, nicméně u grafických procesorů to nebývá pravidlem. GPU Paranoia Karl Hillesland a Anselmo Lastra [2] vyvinuli program - GPU Paranoia 2, který pomocí empirických testů zjistí, jak přesně grafický procesor postupuje při výpočtech s čísly s plovoucí řádovou čárkou. V tabulce 7.1 jsou uvedeny intervaly chyb, které vzniknou při zaokrouhlování na nejbližší hodnotu a intervaly chyb, které vzniknou pouhým ořezáním bitů přetékajících šířku mantisy. Tabulka 7.2 obsahuje intervaly chyb naměřené u čipu R580 a výsledky naměřené v [2] u čipů R300 (ATI) a NV35 (nvidia). Výpočty byly prováděny s čísly o velikosti 32 bitů (1 bit znaménko, 23 bitů mantisa, 8 bitů exponent), v případě čipu R300 je šířka mantisy pouze 16 bitů. operace zaokrouhlení oříznutá (IEEE 754) hodnota sčítání -0.500, 0.500 (-1.000, 0.000 odčítání -0.500, 0.500 (-1.000, 1.000) násobení -0.500, 0.500 (-1.000, 0.000 dělení -0.500, 0.500 (-1.000, 0.000 Tabulka 7.1: Chyba výpočtu - přesné zaokrouhlení a ořezání (ULP) Vzhledem k tomu, že testovací program využívá pro určení chyby pouze vhodné vzory, není možné na rozdíl od zaokrouhlování na nejbližší hodnotu nebo ořezání hranici chyby garantovat. Vzory jsou však vybírány tak, aby měly dostatečně reprezentativní charakter. Z výsledků lze odhadovat, že všechny zmíněné 1 unit in last place 2 na základě staršího programu Paranoia, který byl určen pro univerzální procesory 29

7. TESTOVÁNÍ VÝKONU operace R580 R300 NV35 sčítání -1.000, 0.000-1.000, 0.000-1.000, 0.000 odčítání -0.500, 1.000-1.000, 1.000-0.750, 0.750 násobení (-1.000, 0.001) -0.989, 0.125-0.782, 0.625 dělení -2.000, 1.097) -2.869, 0.094-1.199, 1.375 Tabulka 7.2: Chyba výpočtu - výsledky (ULP) grafické procesory využívají při výpočtech postupy, které jsou založeny na kombinaci zaokrouhlení a ořezání, v žádném z případů však nedosahují výsledků požadovaných normou. Operace dělení vykazují vyšší chyby, což je pravděpodobně způsobeno tím, že dělení je poměrně náročné a v GPU se nahrazuje násobením převrácenou hodnotou. 7.3 Formát dat Při práci s datovými proudy programátora obvykle nezajímá, jaký vnitřní formát data mají. Vzhledem k faktu, že GPU jsou úzce specializovány, však existují instance, které fungují rychleji případně pomaleji v závislosti na tom, jak je pro ně procesor optimalizován. Rozměry proudu BrookGPU mapuje datové proudy na textury, které jsou uložené v paměti grafické karty a na kterých se výpočet provádí. V grafických aplikacích mají textury obvykle čtvercový charakter, z tohoto důvodu je také proces optimalizován pro tyto případy. Odtud plyne, že pro zvýšení výkonu je vhodné místo datového proudu s jedním převažujícím rozměrem použít datový proud, jehož rozměry mají vyrovnaný poměr. Tento odhad se potvrdil i při experimentálním měření (graf 7.1). Testy byly prováděny na následujícím výpočtu: kernel void t_float( float a<>, float b<>, float c<>, float d<>, out float f<> ) { float i, s = 0; for (i = 0; i < 127 && (s >= 0 s <= 0); ++i) s = s + a + b + c + d; f = s; } Při testování jsem se pokusil kromě efektivního zisku změřit také dobu samotného výpočtu (bez datových přenosů). Vzhledem k povaze úlohy a transparentnosti BrookGPU však tento čas nebylo možné změřit přímo. Čistá délka výpočtu 30

7. TESTOVÁNÍ VÝKONU byla tedy odhadnuta z rozdílu mezi celkovým časem výpočtu a dobou, kterou trvá přenos dat mezi pamětí na grafické kartě a pamětí systémovou. Takto upravené výsledky předchozího testu jsou zaneseny do grafu 7.2. Vnitřní formát dat Jednotlivé varianty kódu pro GPU byly vytvořeny ve třech verzích. Výsledek výpočtu je ve všech případech stejný, liší se však vstupní formát dat. První verze přebírá čtyři vstupní parametry typu float, další verze přebírá dva vstupní parametry typu float2 a poslední verze přebírá jeden parametr typu float4. V jazyku BrookGPU vypadá zápis následovně: kernel void v_float( float a<>, float b<>, float c<>, float d<>, out float f<> ) { /* kód */ } kernel void v_float2( float2 a<>, float2 b<>, out float f<> ) { /* kód */ } kernel void v_float4( float4 a<>, out float f<> ) { /* kód */ } Motivací k tomuto testu je fakt, že v grafických aplikacích je přirozené pracovat s vícesložkovými daty (například barvy ve formátu RGBA). Opět lze přepokládat, že varianty s více složkami budou podávat lepší výkon. Výsledky pro testy na výpočtu uvedeném výše v grafu 7.3, respektive v grafu 7.4. Z výsleků je patrné, že v rozporu s předpokladem je nejvyššího výkonu dosaženo při použití typu, který obsahuje pouze jednu složku. Ostatní varianty jsou pomalejší, což demonstruje zejména odhad hrubé síly. Za povšimnutí stojí, že stejný experiment provedený na CPU má pozitivní efekt (graf není uveden). V tomto případě je to způsobeno přítomností vyrovnávacích pamětí, u kterých se zvýšení lokality dat (způsobené v tomto případě použitím vícesložkových prvků) projevuje navýšením výkonu. Je však nutno poznamenat, že u GPU nejsou vyrovnávací paměti použity, protože by byl jejich efekt eliminován velkými datovými přenosy a došlo by tak k degradaci výkonu. 7.4 Velikost dat Tato část se zabývá vlivem objemu dat, nad kterým se výpočet provádí. Proudy jsou při výpočtu mapovány do textur v grafické paměti, maximální velikost proudu je tedy omezena její celkovou velikostí. Navíc však u GPU existují hranice způsobené návrhem čipu, které vymezují dostupné rozměry textur. V případě R580 má největší textura v obou rozměrech velikost 2 048 bodů. Největší možný počet prvků, které 31