VirtualDub ve znamení GPU výpočtů, řada problémů před námi
Avery se v pracích na GPU akceleraci výpočtů dostal konečně do natolik „neostudné“ fáze, aby odhalil, co je hotovo a své plány na nadcházející období. Vězte tedy, že již nějakou dobu pracuje na GPU akceleraci systému filtrů, což by ve výsledku mělo vyústit ve zefektivnění encodingu videa, pokud trochu více filtrujete.
Zpočátku měl Avery velké problémy s podporou formátů, nedostal se dál než na RGB32 a současně musel downloadovat/uploadovat filtry ihned po každé proběhlé instanci. Za poslední rok pracoval na zpracování YCbCr a oddělení filtrů od sebe a čištění systému filtrů do té míry, aby mohl nahodit GPU akceleraci bez toho, aby vzrostl chaos v kódu. V současné implementaci přetrvávají dva problémy.
Prvním je samotné API, které Avery používá, a sice Direct3D 9. To zvolil z několika důvodů: zná jej nejlépe ze všech, debuggování je v něm pro něj schůdnější než jinde, nástroje PIX (zachytává údaje z běžící Direct3D aplikace pro debugging a analýzu) a NVPerfHUD (nástroj Nvidie pro analýzu výkonu pod DirectX) jsou volně k dispozici, běží na každé moderní grafice a shadery mají velmi dobře definované profily, jsou portovatelné mezi různými výrobci grafik a používají standardizovaný byte code.
Na vrchu je 3D portační vrstva, pod ní vrstva filtrové akcelerace (VDXA - nemá to být spíše DXVA?). API nízkoúrovňové vrstvy je designované tak, že může být směrováno jak na Direct3D 9Ex, tak Direct3D 10 i OpenGL. VDXA (budeme se tedy toho značení držet, byť strejda Google o něm také nic neví) vrstva je sice daleko více omezená co do množství funkcí, ale na druhou stranu má daleko jednodušší použití 2D abstrakcí. Filtrační systém ve VirtualDubu byl rozšířen, nyní vkládá filtry, jak je nezbytné pro upload nebo download snímků z akcelerátoru a může iniciovat RGBYUV konverze na grafické kartě. Potud tedy vše v pořádku, ale pouze do chvíle, kdy je třeba vydolovat data zpět z grafické karty.
Existují pouze dvě cesty pro download datových bloků z video karty v DirectX 9 a sice GetRenderData() a „lock and copy“. Z hlediska rychlosti Avery tyto metody popisuje jako pomalou a žalostně pomalou. GetRenderData() je rozhodně preferovanou metodou (výrazně menší zlo :-), v dnešní době dokáže kopírovat nějakých 500 MB/s na každé rozumně výkonné grafice. Problémem však je, že při jejím použití je nemožné udržet CPU a GPU v plynulém paralelním běhu výpočtů, protože GetRenderData() jednoduše zablokuje CPU vždy na dobu, než GPU dokončí všechny přiřazené dílčí výpočty. Výsledkem je, že výpočet na GPU (fakt Avery? nemá to být CPU?) je dost často v bloku a neprovádí se potřebné stahování dat, což drasticky sráží efektivitu. Populární metodou jak toto kompenzovat je použít double-buffering na render target a zpětně číst surface pairs (to fakt nebudu zkoušet počeštit :-), nicméně to Averymu nic neřeší, protože výpočty v jisté fázi stále stojí a čekají na nové výpočty, ať už mají přijít z kteréhokoli render targetu. Avery zkoušel i další metodu, kterak proces zefektivnit, ale k ničemu použitelnému se nedobral, takže nyní bude zkoušet také jiná API, jestli si s tímto zpětným čtením dat neporadí lépe.
Druhým problémem je to, že i po načtení dostatečného množství filtrů, aby se ujistil, že zpětné čtení a plánování úloh nejsou brzdou celého procesu, stále neznamená, že GPU v Averyho počítačích dokáží výkonově překonat normální procesory.
Aktuálně Avery akceleruje přes GPU několik filtrů: invert, deinterlace (yadif), resize, blur, blur more a warp sharp. Při plném zatížení je pět z těchto šesti filtrů rychlejších na CPU o nějakých 20-30% a to i přes to, že Avery cheatoval GPU kód warp sharpu, který tak na GPU používá bilineární samplování místo bikubického. Částečně je důvod v tom, že CPU si svou architekturou s použitými algoritmy rozumí o něco lépe prostě proto, že zpracování 8bitových dat přes SSE2 je 2-4x rychlejší než 32bitových float, protože ta mají 2-4x více paralelismu ve 128bitových registrech CPU. Texture cache v GPU také není zrovna výhodou, protože algoritmus ji nevyužívá tak, jak by bylo optimální a volí jinou cestu. Další část nesrovnalosti výkonu pak padá na tom, že Avery má k dispozici dva stroje, kde je vždy hiend CPU a k němu spíše průměrné až podprůměrné GPU (Pentium M 1,86GHz versus GeForce 6800M - zde CPU jede o 42% rychleji; Core 2 2,5GHz vs Quadro NVS 140M - zde o 213% rychleji, a to na jednom jádře CPU - na tato čísla má ale samozřejmě vliv i řada dalších faktorů jako třeba paměťová propustnost).
Avery hodlá zkusit nějakou rychlejší grafiku (to mi neříkejte, že by mu z AMD nebo Nvidie nemohli něco poslat sakra, když ji jinak k ničemu nepotřebuje, tak si přece HD 4890 nebo GTX 285, nebo nějaký mobilní hi-end kupovat nebude), na druhou stranu si stejně tak může pořídit rychlejší CPU, nebo rovnou čtyřjádro, zkrátka něco na tom GPU kódu prozatím z výkonového hlediska smrdí.
Avery tak zatím vysílá poselství říkající, že ne vždy je GPU výhrou, ani když na něm člověk chce počítat věci, které jeho paralelní architektuře chutnají. Na druhou stranu si přiznejme, že NVS140M se řadí mezi nejpomalejší mobilní GPU poslední generace a navíc je to Quadro, specializované na jiné věci a většina lidí ho doma ani nemá. V ostrém kontrastu k němu pak má Avery k dispozici velice rychlé Core 2 Duo.
Možná se ptáte, proč proboha fosilní verzi Direct3D a ne třeba CUDA či OpenCL a co mnohé další připomínky? Avery odpověděl v diskusi.
Souhlasí, že je daleko jednodušší napsat slušně optimalizovaný shader než CPU rutinu, ostatně to je jeden z důvodů, proč se na pole GPU akcelerace pouští. Pokud to v budoucnu bude možné, rád by vedle GPU kódu měl ještě CPU emulaci. Na druhou stranu, debugging shaderů je náročnější. Na primárním počítači Averymu NVPerfHUD neběží (to není překvápko, GeForce 6800 už je z tohoto pohledu nepoužitelný pravěk), takže je nucen používat výrazně horší PIX, s nímž je to proti debuggingu CPU ve Visual Studiu opravdu frustrující. Debuggovat přímo shadery ve Visual Studiu je téměř nepoužitelné.
Nesouhlasí ale s tím, že shadery nejsou pro filtry vhodné. Naopak jsou velmi dobré pro konvoluční filtry, které mohou implementovat velkou škálu výpočetních algoritmů, jsou tam ale některé dílčí problémy, které jsou údajně u Xboxu 360 řešeny výrazně elegantněji.
Důvody proč nepoužít Nvidia CUDA, jsou jasné: CUDA běží pouze na GeForce, a to pouze řadě 8 a vyšší. Dále má specifický kompilační formát, který činí nemožným použít CUDA filtry na jiném API (divné, Simon Green z Nvidie nám naopak v lednu potvrzoval, že přenositelnost je velice dobrá, minimálně na OpenCL). Dále zmiňuje debuggovací problémy, kdy sice lze CUDA kernel překompilovat tak, aby běžel na CPU, ale to lze pouze pro high-level API, zatímco Avery by potřeboval použít i low-level. No a nakonec je tu nedefinovaný threadovací model, kde Avery potřebuje spouštět a zastavovat akcelerační engine v pracovním threadu, což nemusí být stále ten stejný a toto CUDA neumí zaručit.
OpenCL se Averymu jeví daleko zajímavější, ale není zatím široce podporováno. Implementace Nvidie je aktuálně uzavřená beta (pozn.: Nvidia už Khronosu zaslala svou finální OpenCL implementaci k certifikaci). Program pro non-embedded systémy také má být pouze ve formě zdrojáků, což je poněkud děsivé.
Avery plánuje celou tuto GPU filtrovou mašinérii nakonec realizovat jako plugin API, kdy každý, kdo napíše filtr pro VirtualDub, bude mít svůj filtr běžící na GPU, takže požadavky na daleko přísnější než na samotný VirtualDub. Avery zkrátka potřebuje API, které je široce dostupné na všech systémech, jednoduché na programování a bude dostupné i v daleké budoucnosti. Žádat po autorech pluginů, aby svůj kód psali několika různými způsoby a testovali na pěti různých počítačích, je nepřijatelné, API musí být jediné a jednotné.