12.6.2016

foto Petr Bravenec

Petr Bravenec
Twitter: @BravenecPetr
+420 777 566 384
petr.bravenec@hobrasoft.cz

V listopadu jsem lektoroval školení pokročilých technik v Qt. Při přípravě materiálů jsem prostudoval množství různých částí Qt, o kterých jsem měl dosud jen matné představy. Jednou z těchto částí je paralelní zpracování.

K paralelním výpočtům už jsem se párkrát dostal. Poprvé to bylo při statistickém zpracování velkého množství dat z fotovoltaických elektráren. V dokumentaci Qt jsem na prostředky pro paralelní zpracování tehdy narazil, ale kvůli nulové zkušenosti s paralelními výpočty jsem vůbec nepochopil, jak knihovnu Qt pro paralelní výpočty použít. Paralelní výpočet jsem proto udělal jednoduše ve vláknech a řízení celého výpočtu jsem naprogramoval ručně.

Výsledkem je aplikace, která sice dokáže využít výpočetní kapacitu na 100% - na efektivitě zpracování dat se moje ruční práce nijak negativně neprojevila. Nemusel jsem se však vůbec zabývat řízením paralelního výpočtu a aplikace mohla být v této části jednodušší a přehlednější. Řízení paralelního výpočtu dokáže zajistit knihovna Qt sama.

K hlubšímu pochopení problematiky paralelních výpočtů jsem se propracoval při používání databáze CouchDB a ještě později při snaze využít pro paralelní výpočty grafickou kartu.

Odkazy

QtConcurrent

V Qt se pro paralelní výpočty používá modul QtConcurrent. Celý modul je tradičně výborně popsaný, ale bez základní představy o paralelních výpočtech může být poněkud matoucí.

Vstupní data

Základem pro paralelní výpočty je množina vstupních dat. Co si pod tím představit? U statistických výpočtů na fotovoltaických elektrárnách je nejmenší samostatně zpracovatelnou položkou jeden den na jedné fotovoltaické elektrárně. Pokud dělám porovnání deseti různých elektráren v průběhu celého roku, dostanu 3650 vstupních hodnot (365 dní krát 10 elektráren). Každá z těchto vstupních hodnot se dá zpracovat samostatně a nijak neovlivňuje výpočty odvozené od ostatních vstupních hodnot.

Výpočetní funkce

Výpočetní funkce převádí vstupní data (365 dní krát 10 elektráren) na výstupní data (3650 hodnot s denními statistikami jedné elektrárny).

Výpočet statistiky jedné elektrárny v jeden den může být poměrně komplikovaná záležitost. Na začátku výpočtu se připojím k databázi a načtu naměřená data příslušná k elektrárně a dni. Naměřených hodnot může být u jedné elektrárny několik desítek až stovek tisíc. Načtená data se statisticky zpracují a výsledkem je několik statistických parametrů charakterizujících práci elektrárny v daný den - několik stovek různých údajů, pomocí kterých lze jednotlivé elektrárny porovnávat.

U tohoto výpočtu je důležité, aby nebyl nijak závislý na ostatních probíhajících výpočtech nebo jiném kontextu aplikace. Při výpočtu se nelze podívat na jinou elektrárnu nebo jiný den. Pokud je výpočet takto postavený, dá se většinou naprogramovat docela snadno a bez potíží.

Dále je nutné zajistit, aby nebyl jeden výpočet závislý na jiném kontextu aplikace. Co to znamaná? Například to, že pro načtení dat z databáze je vždy nutné vytvořit nové připojení. Jedno společné připojení totiž nemůže zpracovávat najednou několik požadavků. Po ukončení výpočtu je potřeba spojení k databázi uzavřít. (Velkou výkonovou výhodou je samozřejmě samostatný databázový stroj s dostatečným počtem procesorových jader, alespoň u databáze PostgreSQL).

Díky tomu, že je výpočetní funkce samostatná, nezávislá na vstupních údajích pro jiná vlákna a na kontextu aplikace, je možné spustit několik takových výpočetních funkcí najednou - výpočet tak probíhá paralelně.

Velikou výhodou výpočetní funkce v QtConcurrent ve srovnání s OpenCL je rozsah funkce a množství dat, jaká může výpočetní funkce zpracovat. Výpočetní jádro v OpenCL (grafická karta) může mít obvykle jen velmi omezenou velikost a omezené množství paměti. Navíc lze při programování výpočetní funkce v QtConcurrent využít prakticky veškeré možnosti jazyka C++ a knihovny Qt. Omezením může být nižší počet výpočetních jednotek a jejich nižší výkon - na svém počítači můžu pro výpočet využít maximálně 8 jader AMD procesoru (toho nejvýkonnějšího, jaký jsem si troufnul koupit), nebo 14 výpočetních jader v grafické kartě (v té nejprůměrnější, za kterou mi nebylo líto utratit kopu peněz).

Implementované algoritmy

Modul QtConcurrent v Qt implementuje několik základních algoritmů pro provádění paralelních výpočtů:

  • Map
  • Filter
  • Map-Reduce
  • Filter-Reduce
Map

Výpočet popsaný výše (statistika z fotovoltaické elektrárny) je typickou implementací algoritmu map. Na všechny prvky vstupní množiny (365 dní krát 10 elektráren) se aplikovala nějaká operace (načtení naměřených hodnot a výpočet statistiky) a výsledkem je struktura popisující chování konkrétní elektrárny v daný den. Data ze vstupní množiny byla pouze převedena do jiné podoby. V dalším zpracování v aplikaci lze výstupní data například porovnávat mezi sebou, zobrazovat v grafu a podobně.

Filter

Fotovoltaické elektrárny pracují typicky v různě proměnlivých povětrnostních podmínkách. Je obtížné srovnávat elektrárny mezi sebou, pokud bychom se snažili například porovnat elektrárnu, na kterou pražilo slunce, s elektrárnou, kde celý den propršelo. Algoritmus filter dokáže z množiny vstupních dat vybrat pouze takové údaje, které odpovídají zadaným podmínkám. U elektráren by to byly například jen dny, ve kterých svítilo slunce převážnou většinu dne. Takových dní bývá jen několik v roce a najít je v záplavě dat ručně může být dosti pracné.

Funkce implementující takový výpočet by se v principu příliš nelišila od výpočtu denní statistiky jedné fotovoltaické elektrárny. Na výstupu by se však místo statistických údajů o práci elektrárny v daný den (tj. několik desítek až stovek různých parametrů) objevila pouze jediná hodnota: zataženo/jasno (true/false).

Map-Reduce

Představte si, že chcete vyhodnotit práci fotovoltaické elektrárny v celém roce a shrnout výsledky do jediného čísla (nebo do několika málo hodnot). Takovým číslem může být například porovnání celkové roční výroby s předpokládanou výrobou nebo porovnání s údaji o osvitu daného území z družicových měření.

V prvním kroku takového výpočtu se provede zpracování naměřených údajů za každý den a elektrárnu. V paralelním výpočtu jsou vstupní data (356 dní krát 10 elektráren) převedena na výstupní statistiku (opět 365 dní krát 10 elektráren).

Následně se výstupní data agregují funkcí reduce - ze všech hodnot se spočítá souhrnná statistika po jednotlivých elektrárnách za celý rok. Výsledkem je deset výstupních hodnot, pro každou elektrárnu jedna výstupní hodnota (hodnotou se může rozumět několik desítek až stovek různých údajů v nějaké struktuře).

Filter-Reduce

Předchozí popsaný algoritmus - map-reduce - dokáže spočítat data po jednotlivých elektrárnách za celý rok. Započteny jsou dny, ve kterých svítilo slunce, ale i dny, kdy bylo zataženo. Pokud bychom chtěli do výpočtu zahrnout pouze údaje ve slunečných dnech, mohli bychom množinu vstupních dat zúžit pomocí paralelního algoritmu filter a teprve nad touto množinou vytvořit celkovou statistiku za všechny slunečné dny.

Poznámka: výsledkem funkce filter je informace ano/ne (slunečno/zataženo). Je mi jasné, že takto naivní výpočet nad zúženou množinou slunečních dní by musel zopakovat některé již jednou provedené výpočty. Vzhledem k tomu, že již vím, kolik práce mají dva hodně výkonné počítače s takovým výpočtem (databázový server a výpočetní server), nutnost opakování některých výpočtů mne děsí.

Závěr

Tento článek by měl být pouze úvodem k seriálu o paralelním programování v Qt. Sledujte tyto stránky, sledujte náš Twitter. Další díly budou následovat.

Hobrasoft s.r.o. | Kontakt