OpenMP lassít?

 ( horvatha | 2008. március 3., hétfő - 20:37 )

Kedves HUP-osok!

Egy régi kódomat próbáltam begyorsítani az OpenMP segítségével egy Intel P4 dual core gépen, Ubuntu 7.10 alatt a gcc-4.2-t használva.

Elvileg menni kellene, mert a szerkezet triviális, a magja ez:

#pragma omp parallel for private(i_met)
for (i_met = 0; i_met<10; i_met++)
{
ga_met (0.5*(float)i_met/10.0);
}

A ga_met rutin nem használ globális változókat, teljesen függetlenül fut különböző argumentumokkal.

Ha lefordítom OpenMP nélkül, és "time ./ga_teszt" -tel futtatom, ezt kapom:
real 1m3.871s
user 1m3.036s
sys 0m0.048s

OpenMP-vel: (-fopenmp a fordításkor)
real 1m50.932s
user 2m2.632s
sys 1m2.212s

OpenMP nélkül a top szépen 98-100%-ot ad futás közben a processznek, OpenMP-vel 170-190%-ot, tehát úgy csinál, mintha párhuzamosan futna két szál. És az egyes ga_met() hívások eredménye sem emelkedő sorrendben jelenik meg a kimeneti fájlokban. Valami párhuzamosság tehát van....

Mi lehet a hiba oka? Sok doksit összeolvastam, de ennek a szimpla esetnek menni kellene így és nem lassabban, mint a szekvenciális verzió!

Előre is köszi!

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

Probald meg az OMP_NUM_THREADS környezeti változó erteket beallitani 2-re. Az ennyire elszálló sys érték jelentheti azt, hogy túl sok szálat indított el az openmp. Másik ok lehet az, hogy a programod nem CPU intenzív így lehet, hogy csak éheznek a magok. Először egy oprofile lehetne a te barátod, hogy kiderüljön, mi a helyzet...

Kösz a választ. Bár szerintem nem ez lehet a magyarázat.... De lehet, hogy tévedek.

"Probald meg az OMP_NUM_THREADS környezeti változó erteket beallitani 2-re.":

Mindjárt megpróbálom. De a külső jelekből arra következtettem, hogy rájön magától, hány procim van.

[Szerk: megpróbáltam, nem oldja meg.... :-(( ]

"a programod nem CPU intenzív"

De. Számdarálás: lebegőpontos műveletek sora. Ez nem lehet a baj.

OMP_NUM_THREADS: ki is exportaltad?

Attol meg, hogy lebegopontos muveletek sora, meg siman lehet memoria korlatos

for(int i=0;i<1000000;i+=8)
c[i]=a[i]+b[i];

pl. lebegopontos muveletek sora megis erosen memoriasavszel korlatos.

Azert ajanlottam az oprofile-t, mert ott meg lehetne csekkolni, hogy pl. mennyi cache miss van, mekkora a fuggvenyek cpi-je.

"OMP_NUM_THREADS: ki is exportaltad?"

Igen. :-)

"Attol meg, hogy lebegopontos muveletek sora, meg siman lehet memoria korlatos"

OK, de --mint írtam-- a meghívott rutin (ga_met) példányai egymástól függetlenül futhatnak, mert csak lokális változókat használnak és (ezt még nem írtam) nem használnak nagy adatterületet (4-5 MB), viszont azt sokszor "átfésülik" a számítás során.

"Azert ajanlottam az oprofile-t,..."

OK, ezt nem ismerem még, megnézem.

Kösz!

" nem használnak nagy adatterületet (4-5 MB)"

Ez már lehet nagy, főleg ha kellően összevissza van szükséged az adatokra, vagy ha egyszerűen nem sorfolytonosan éred el az adatokat, stb.

Szerintem 3 dolog lehet:
1) Belefutottál egy OpenMP bugba (nem hiszem, bár gcc-ben elég új dolog)

2) Valami rosszul van beállítva (OMP_NUM_THREADS-en kívül nekem sem jut más eszembe... omp_get_num_threads-t lekérdezhetnél esetleg...)

3) tényleg memóriaintenzív a kódod. Ezt többféleképpen leellenőrizheted:
- valami programmal ami nézi a cache-miss-t
- utánaszámolsz, hogy kb hány szorzás, összeadás, osztás van egy átlagos futás alatt, utánanézel, hogy átlagban ezek hány órajelig tartanak (ha nem kell várni a memóriára), ezzel súlyozva összeadod, és ha ez nagyságrendekkel kevesebb, mint a procid teljesítménye, akkor lehet gyanakodni. :)
- sima threadet használsz... jó tudom, pont ezt akartad elkerülni, de az eseted annyira triviális, hogy nagyon egyszerű kettébontani a ciklust, és lefuttatni külön threadben. Ha ekkor is lassú, akkor nem az OpenMP-ben van a hiba.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

A 4-5MB lehet, hogy már épp nem fér be a cache-be. Így az átfésülések között csomó memóriaművelet lesz, ami lassú.

Nem tudom milyen architektúrád van, de ha a két mag között közös a cache-es és a két elindított szál különböző adatokon dolgozik, akkor a két szálat egyszerre indítva érthető módon csökkenni fog a teljesítmény egymás cache-ének a rontása miatt.

Ha a cache-be bele tudnád optimalizálni az adataidat (hogy beleférjenek), akkor viszont a soros futtatást is meg tudod gyorsítani.

Fogalmam sincs, hogy egy mai csúcsproci mennyi cache-sel jön, de az enyém pl 1M. Ez kevésnek tűnik a 4-5M-hez képest.

Hallottam valami urban legendet, hogy egy Inteles fószer ránézett egy ilyen memóriaintenzív kódra, a blokkméreteket hozzáigazította a cache-hez és többszörös teljesítménynövekedést ért el. Úgyhogy mindenképp érdemes elgondolkodni ezeken a kérdéseken.

Munkam soran eleg gyakran latok ilyen eredmenyeket. Sajnos egy compiler nem rendelkezik eleg tudassal ahhoz, hogy kepes legyen az adott architecturara optimalizalni a kodot. Ez nem jelenti azt, hogy assemblyben kell csinalni, mindossze arrol van szo, hogy a hotspotokat tudatosabban kell megirni.