Vyzkoušel jsem si kódy z repozitáře pana autora a dovolím si rozebrat nebo doplnit některé informace z článku.
(Určitě se podívejte na odstavec 4)
Testoval jsem nativní implementaci v C a numba7 primárně na velikosti 4096 a 25500 iteracích.
Stroje (všechny nastaveny 1 vlákno na jádro)1) Pokud chceme měřit skutečnou rychlost výpočtu, tak dle mého názoru, měřit celkový běh programu nedává moc smysl a mnohem přínosnější je měřit jen čas provedení výpočetní funkce, protože zápis na standardní výstup je celkem pomalý. A to například pomocí gettimeofday() z knihovny sys/time.h a pro python time.time() z knihovny time. Například na stroji 3. trvá nativní výpočet 3.514 sekund a tisk na stdout 2.771 sekund, takže IO prodlužuje dobu běhu programu o cca 78%. Je sice pravda že tam to zdržení je a bude, ale dle mého názoru není zajímavé dokud nebudeme optimalizovat IO.
2) Ofast, Obad, aneb velmi rychlá střelba do nohy. -Ofast v nativní implementaci nepřináší téměř žádný další výkon oproti -O3. Navíc při dalším rozšiřování programu se dřív nebo později projeví chybným nebo nezvyklým chováním programu.
Myslím že lepší volbou jsou například -mtune=native a -march=native
3) Sice Amdahlův zákon nás omezuje, ale naštěstí je tu Gustafsonův zákon. Jednoduše řečeno, Amdahlův říká něco jako že pokud v programu je nějaká sekvenční část, tak více jader nepomůže. Gustafsonův zákon na to jde obráceně a říká něco ve smyslu, že pokud máš příliš mnoho jader, tak zvětši řešený problém a zachováš si tím zrychlení. Tyto zákony se objevují taky pod pojmy slabé a silné škálování.
4) Výrok: "výpočet ve více vláknech v C lze realizovat mnohem složitěji" Není pravdivý. Již nějakou dobu je tu s námi knihovna OpenMP, která ve světě C/C++ působí jako zázrak, protože pomocí jednoduchého makra
#pragma omp parallel for paralelizujeme naší smyčku. Pak stačí jen kompilovat kód s flagem -fopenmp a mít v systému balíček openmp. Nejjednodušší použití vypadá například takto
#pragma omp parallel for for (int y = 0; y < height; y++) {
což vypadá mnohem více zadarmo než
@jit((int64, int64, int64, UniTuple(UniTuple(int64, 3), 256)), nopython=True, parallel=True) def calc_mandelbrot(width, height, maxiter, palette):Samozřejmě musíme stejně jako v případě numby vnořit proměnnou cy dovnitř prvního cyklu
5) Zde je menší porovnání
C ~ 1 jádro,
OpenMP ~ všechny jádra,
Numba ~ všechny jádra
stroj | 2048 4096 __________|_____________________ 1. C | 61.73 246.7 1. OpenMP | 16.80 67.21 1. Numba | 32.55 130.9 | 2. C | 67.52 269.8 2. OpenMP | 6.951 27.68 2. Numba | 19.28 76.72 | 3. C | 54.24 217.6 3. OpenMP | 0.883 3.514 3. Numba | 2.800 11.10
Na závěr bych chtěl dodat, že mě velice mile překvapilo kam až to python výkonově dotáhl.