Fejlesztgetek 1 programot, szem elott tartva hogy platformfuggetlen legyen. Tegnap az egyik modul stresztesztjet leforditottam linuxra is, es nehany overflow fixalasa utan (fokepp a CLOCKS_PER_SEC es a RAND_MAX nem kis kulonbsegebol adodtak) nagyon nem jo eredmenyek jottek ki gcc-vel forditva.
Optimalizacio nelkul jobb a gcc:
(mivel a tesztprogram random adatokkal tesztel tobb menetben igy a legjobb-legrosszabb futasi idoket adom meg)
MSVC: 4938-5219 ms (/O0)
gcc: 3510-2970 ms (-O0)
Optimalizacioval:
MSVC: 32-31 ms (/O2 /Ot)
gcc: 260-300 ms (-O3 -march=k8)
A kerdes gondolom adott:
En cseszek el valamit nagyon a gcc-s forditasnal, vagy a gcc optimalizacioja sikerult ennyire gyengere?
Amennyiben az elobbi, akkor mit rontok el?
Amennyiben az utobbi, akkor mit hasznaljak gcc helyett?
Meg annyi hogy a modul intenziven hasznal stl::list-et, szoval akar a linuxos (default) stl is lehet a hunyo.
Hozzászólások
up
Hát így elég nehéz bármit is mondani, de javasolnám a gprof használatát, azzal eléggé jól le lehet követni, hogy hol fáj a programnak. Én megnézném, hogy a gcc-s fordításnál az összes debug (pl. -g -ggdb, stb.) és profiling opció ki van-e kapcsolva. Az STL-nek is van egy előredefiniált preprocesszor makrója (amit fejből meg nem mondok, hogy mi a neve), ami megmondja, hogy kell-e ellenőrzéseket fordítani a template-ek kódjaiba vagy sem. Az is rengeteget számít.
Tapasztalataim szerint a gcc által produkált kód alapesetben nem sokkal (pár százalék) marad el az MS-es fordítóétól. Nagyobb ez a különbség, ha az msc-nél használsz profile-guided optimizationt (+10..20%). Ilyet a gcc is tud, csak én még nem láttam működni. :-/
-------------------------------------------------
" - Amerikanische Infanterie! Angriff! Angriff! "
A makefile igy nez ki:
CFLAGS-et a make-nek adom at forditaskor:
make CFLAGS="-O3 -march=k8"
http://gcc.gnu.org/onlinedocs/libstdc++/17_intro/howto.html
ennyit talaltam, ott van vagy 5 makro amivel be lehet kapcsolni bizonyos debug dolgokat. Alapbol mind undefined, en meg nem definialom oket.
gprof-ot meg nem probaltam, nem tudom mennyire segitene, MSVC-ben sem trukkoztem semmit, a kod megis majd' 10x gyorsabb, pedig ott meg architekturat se nagyon lehet megadni.
Eselteg az alábbi kapcsolókat próbáld meg, hátha valamelyik segít:
-fomit-frame-pointer
-fno-gcse
-fforce-mem
-malign-functions=4
-mpreferred-stack-boundary=4
Amúgy érdemes gprof-al megnézni, mert nem bonyolult, és cpu időigény szerint ad egy listát, amiből azonnal látod, hogy hol a gond. Nem kell több nap megérteni.
-------------------------------------------------
" - Amerikanische Infanterie! Angriff! Angriff! "
Gondolom az nem kérdés, hogy szükséged van-e egyáltalán az optimalizációból adódó sebességelőnyre?
adott esetben az a tevekenyseg aminek egy resztevekenysege ez, amivel merek, meglehetosen hard realtime bir lenni. Ennek ellenere az algoritmus sajnos NP teljesnek tunik bizonyos esetekben
icc talán.
Milyen gcc milyen OS mutat ilyet ?
Nem induláskori az az idővesztés ?
ja, ezt gondoltam en is, hogy nem indulaskori-e ez az idoveszteseg.
eleve a teljes benchmark ideje szinte a meresi hiba hataran belul van.
- Use the Source Luke ! -
nem. a merest maga a szoftver vegzi ott, ahol az idoigenyes tevekenyseg indul. szoval nem time ./test, hanem a kodban van idolekeres a fo loop indulasa elott es utan. ott meg linuxon a kernel tick hatarozza meg a legkisebb merheto egyseget, ami 1/250-ed vagy 1/300-ad sec, mar nem emlekszem mire van allitva a kernelparameterekben a tick. Winen meg performance counterrel mer a program, ami orajel nagysagrend.
Magyaran a mert ertek nagyon messze esik a meresi hiba hataratol. Raadasul a gepek amiken probaltam 2 magos procival futnak, ugyhogy eleg jo esely van ra hogy a
Lebegő pontos a kód ?
Milyen függvényeket hivogat ?
Templateket használsz benne ?
Több szálas ?
Lehet, hogy msvc hatalmas kodreszleteket kiegyeszerűsített a programodból ?
fixpontos. a muveleteket egy 4 integer membert (left, top, rigt, bottom) tartalmazo "rect" nevu classon kell vegezni. ezek egy stl::list-ben vannak tarolva, mivel ugyis szekvencialisan kell tudni nagyon gyorsan vegigmenni az osszesen, ez ujfent gyorsan kell tudni az egyik containerbol egy masikba atdobni az elemeket.
OS szinten? kb annyit amennyit a stl::list hiv, azaz memoriamanagement uj elem letrehozasakokr.
szeinted? :-P
nem.
szinte kizartnak tartom. eleg egyszeru a kod en meg messze nem vagyok mar nullkilometeres c++-ban.
maga a feladat, egy nem elforgatott, adott esetben egymassal fedesben levo teglalapokkal lefedett terulet, nem fedesben levo teglalapokkal lefedese. sajnos nem jutott eszembe hatekonyabb megoldas mint a teglalapokat egyenkent hozzaadni a keszlethez, es minden hozzaadaskor optimalizalni a hozzaadott teglalapot (bizonyos esetekben ez csak az uj teglalapo ketteosztasaval oldhato meg)
a ket teglalap iptimalizaciojat a rect::optimize oldja meg, ezt hivja eleg sokat a rectset::addRect, amit a teszter hiv meghatarozott szamu random teglelappal.
itt van a rect es a rectset deklaracioja:
itt meg a kod:
Van meg kerdes?
Én a rects.cpp-t is .h-ba raknám, nem feltétlen a rects.h-ba, maradhat külön, csak az inline-osítás miatt. Azon elvileg sokat lehet nyerni, főleg ha rövid, és gyakran meghívott fv-ekről van szó...
Ez persze nem válasz az msvc-gcc különbségre...
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o
Apróbb megjegyzés, hogy az osztály deklarációjakor definiált fv-ek elé nem kell "inline" kulcsszó, mert azok már azok.
Tehát elvileg a
és a
ugyanazt eredményezi...
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o
kelleni lehet hogy nem kell, de artani biztosan nem art, a kod olvashatosagat viszont szerintem javitja.
Nekem zavarja a szemem, de ez nyilván megszokás kérdése...
Mondjuk én jobb szeretem a deklaráció után megadni az inline fv-eket...
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o
a gyakran hivott rovid fuggvenyeket kiveve a header-be es inline-ra deklaralva minimalis a merheto kulonbseg.
Azért az az optimize, meg a hasIntersection elég gyakran hívott fv-ek. Főleg ez utóbbi adbná magát inline-nak...
A kérdés egyébként azért is érdekes, mert pl nem tudom, hogy msvc mennyire agresszívan inline-osít kérés nélkül. Egy próbát talán megér.
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o
Újra csak nem a kérdésre a válasz :) :
Érdemes lenne megnézned gproof-fal vagy AMD Codeanalysttel, hogy mi tart sokáig, mert ha kiderül, hogy a listaműveletek (memóriafoglalgatás) akkor lehet, hogy nem helyben kéne a listát kezelni, hanem két vektorral az egyikből pakolni a másikba...
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o
Ezzel mindossze az a bajom hogy jelen esetben nem alapvetoen a kod felgyorsitasa a kerdes, hanem az, hogy miert general majd 5-10x lassabb kodot a gcc, mint az MSVC. Ha a list rossz valasztas lene, akkor rossz valasztas lenne MSVC-hez is.
Elobb-utobb persze nem uszom meg hogy megismerkedjek a gprof-fal, de most nem aldoznek ra napokat.
Ha valami oknál fogva linux alatt lassabb a memóriafoglalás, az megmagyarázná a dolgot...
Egyébként elég furcsa, mert a gcc nem egy rossz fordító, ha megnézel benchmarkokat akkor kiderül, hogy az icc-hez és az msvc-hez képest legrosszabb esetben sem lassabb 10%-nál, és nagyritkán még nyer is...
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o
hat nem tudom. win alatt mingw-vel forditva is ugyan ez a helyzet.ezzel szerintem a platform kulonbsege nagyjabol ki is lett love, ami miatt esetleg a memoriafoglalas lassabb lehet az a gnu libc kontra MSVC libc, de ekkora kulonbseget ez sem lehet magyarazat szerintem.
Ami mint otlet felmerult az az hogy megprobalok megmerni kulonbozo stl es nem stl muveleteket MSVC/w32 kontra G++/Linux kozott
Meglepo eredmeny szuletett. Benchmark: hozzaadunk egy listahoz kuuuuvasok integert aztan vegigiteralunk rajra ugy hogy fel is hasznaljuk az erteket, aztan meg felhasznalas nelkul:
Eredmenyek
gcc:
8388608 integers added to the list in 670 miliseconds
iterating through the list with access took 50 miliseconds
iterating through the list took 50 miliseconds
msvc:
8388608 integers added to the list in 7723 miliseconds
iterating through the list with access took 131 miliseconds
iterating through the list took 131 miliseconds
Ezek szerint lassu gcc-s stl kilove. De akkor mi a franccal nem birkozik meg a gcc optimalizacioja? (jo, tudom, gprof, de meg soha nem hasznaltam es alapvetoen nem fogja megadni azt az infot hogy MSVC-hez kepest mit csinal lassabbra a gcc, mert ahoz az MSVC-vel forditott kod profilingja is kellene)
http://hup.hu/node/44803#comment-433357
Probáld így.
A teszten belül van más is azon kivül amit bevágátál ? pl. I/O, string (sstream) ?
icc-vel megprobálod ?
Ebbe semmi olyan nincs amit gcc -ne tudna elvileg optimalizalni. stl implementációban lehet baj esetleg.
Elküldesz annyi kódot, hogy reprodukálni tudjam a tesztet ? +assembliben amit msvc csinált.
Esetleg egy próbát megér az idő ilyetén lekérdezése: getrusage(RUSAGE_SELF, &r_usage), r_usage.ru_utime, r_usage.ru_stime.
Egy próbát szintén megérhet az assembly vizsgálata (bár ehhez a gprof kellene, hogy mit érdemes nézni egyáltalán): g++ -g -O3 ..., objdump -S.
Esetleg mérd meg külön, mennyi időt töltesz az std::list metódusaiban.
Az std::list metódusai mennyire inline-olódnak helyből? A következő (mint tipp) csak vaktában lövöldözés, de ki tudja (-fvisibility=hidden, -fvisibility-inlines-hidden):
http://gcc.gnu.org/wiki/Visibility
gcc -O2 ?
Az -O3 ugyanis nem feltétlenül jobb mint az -O2. Egy próbát megér...
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o
-O2-vel bar nem lenyegesen, de merhetoen lassabb.
Még mindig kérdés a OS típusa, és a Gcc verziója.
compi@arwen-d32:~$ uname -a
Linux arwen-d32 2.6.23-rc8-k8i-pae #1 SMP PREEMPT Wed Sep 26 10:23:38 CEST 2007 i686 GNU/Linux
compi@arwen-d32:~$ gcc --version
gcc (GCC) 4.2.1 (Debian 4.2.1-3)
Hát igen. Akkor ez a baj. Ha i486-ra optimalizált gcc-vel optimalizálsz, ne várj optimális teljesítményt. Próbálj forrásból forgatni egy gcc-t, amit a processzorodnak megfelelőenj optimalizálsz ez alapján.
Még optimálisabb teljesítmény jöhet ki, ha esetleg valamilyen forrásalapú disztrót is kipróbálsz, például Gentoo-t, vagy GoboLinuxot.
jaj nem. azert mert 486-osra van optimalizalva, csak lassabban fut a gcc, de ugyanugy tud optimalizalni akarmire, amit megadsz az -mcpu= -val (azt hiszem azzal).
- Use the Source Luke ! -
-mtune illetve -march.
Az -mcpu már "kiment a divatból"...
De különben jól mondod.
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o
Ez igaz, de ha nincs semmi optimalizáció megadva, akkor a saját defaultjait veszi alapnak asszem. Nem tudom, de empirikus úton láttam már forrásból feltett programokat beteg rajzolt bivaly módjára futni deb alatt.
Ebben viszont igazad lehet, ilyenre nem is gondoltam.
Meg az igazsaghoz hozzatartozik, hogy a kerdezo megadta az -march=k8-at.
- Use the Source Luke ! -
ZOMG
--
Bow down and admit defeat. | Old, weak and obsolete.
kivételesen egyetértek
---------
"Ha igazat mondasz azt végig unják, ha feldíszíted azt jól meg dugják"
szerény blogom -- új címen!
gcc verzió?
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o
próbáld meg csak assembly kódig fordítani, és nézd meg a két fordító esetén a fájlok méretét
---------
"Ha igazat mondasz azt végig unják, ha feldíszíted azt jól meg dugják"
szerény blogom -- új címen!
Na sikerult vegre kideriteni mi okozza a problemat: az elemeket egy vizsgalat utan 2 listaba szedem szet, megpedig splice-szal. Ez a muvelet egy while loopban van, ami addig megy amig van elem az eredeti listaban:
aminek eszement koltsege van a gcc stl implementaciojaban az a list::size(). Istenverte elmeroggyant gnu fele stl list::size() implementacio mit csinal? na mit? OMG:
return std::distance( begin(), end() );
Ami bizony-bizony szepen, komotosan egyesevel vegiggyalogol a lista elejetol a vegeig es megszamolja oket. Erre tenyleg csak annyit tudok mondani hogy attttyauuuuuristen.
ugyan ez a fuggveny M$ fele implementacioban ennyi:
return(_Mysize);
hat ezzel kb veget is ert az X akta, ha az empty()-t hasznalom a size() helyett akkor normalis sebesseggel fut a dolog.
Ezzel a modositassal 180-200 ms alatt vegez a gcc altal generalt kod ott, ahol az MSVC-vel forditott 357-342 ms alatt.
Szerintetek sirjak valami levlistan (es ha igen melyiken) hogy ez a splice() igy nem teljesen koser, vagy hagyjam a francba az egeszet?
> aminek eszement koltsege van a gcc stl implementaciojaban az a list::size().
Nincs olyan, hogy list::empty() ? Csak mert a while(){} -ba ez illik inkább.
Ez kicsit ilyen:
http://www.brainhz.com/underhanded/results2006.html
(Underhanded C Contest 2006-os gyoztese)
---------------------
AFPer: We've missed you, did you miss us?
Pratchett: Yes, but I think I have time to reload.
mondjuk annyi kulonbseggel hogy eszembe nem jutna egy conatiner classban az ilyet nem letarolni es keres eseten a letarolt erteket visszaadni (ahogy az msvc stl implementacioja teszi) hanem minden egyes size() hivasnal vegigiteralni az osszes elemen (ahogy a gcc stl implementacioja teszi) egyszeruen nincs ra epeszu indok.
This choice is permitted by the C++ standard. The standard says that size() "should" be constant time, and "should" does not mean the same thing as "shall".
Komolyan gratulalok, politikusnak kellett volna mennie az ilyennek, nem fejlesztonek. Ott asztan el lehet vitatkozgatni mit jelentenek az egyes szavak valojaban, es maris nem kell a problema megoldasara koncentralni.
Es meg ha el is fogadom az indokaikat hogy a szabvany csak annyit mond hogy "kellene" es nem "kell", ezert ok ugy dontottek hogy a size() konstans sebesseget felaldozzak a splice() sebessegeert (a 3 splice() hivasi modbol 1 eseteben valoban nem tudhato hany elemet is mozgatunk at ha nem akarunk vegigiteralni az elemeken) akkor is van egy olyan megoldas hogy jelzem egy flaggel hogy az eltarolt ertrek invalid es a kovetkezo size() hivasnal aktualizalni kell.
Mondjuk ha a c++ szabvany valoban igy fogalmazza meg, akkor nekik is lo..sz a s...ukbe. Valahogy nekem a szbavany szo hallatan nem az olyan kifejezesek jutnak eszembe hogy "kellene", "lehet" meg "esetleg". Egy szabvanyositott nyelv szabvanyositott alapkonyvtaratol szerintem joggal lehetne elvarni hogy implementaciotol fuggetlen legyen a viselkedese barhol. Pont az az egyik hasfajasom az MSVC-vel hogy sok esetben tojik a szabvanyra. Pont a gcc fele stl implementaciotol nem varnam azt hogy a shall es a should jelentesen kezdjen el vitatkozni ahelyett hogy rendesen leimplementalna azt az istenverte osztalyt.
Karban tartni size valtozot koltseges, es legtobb esetben felesleges, neked sincs ra szukseged is_empty() pont eleg.
Ezrert nem szeretem ezt Template meg OO dolgot, mert az ember altalabna nem tudja mi zajlik a haterben, ill., ha valami hiba egy templaten belul csapodik le azt nehez debeuggolni.
Azért néha azh ember lekérdezi a size()-t... Én nem szeretem a melegvízet folyamatosan implementálni... De amúgy kb. igazad van.
Miertis? A ketiteratoros muiveleteken kivul (ilyen verzioja a splice-nak, assignnak, es az erase-nek van) van szerinted olyan fuggvenye a list()-nek amikor nem tudod minden varazslas nelkul hogy hany elemmel noveled/csokkented a listad elemszamat? Ha meg ilyet hivsz bebillented a _size_invalid flagedet az osztalyban es az elso size() hivaskor jobb hijjan vegigiteralsz az elemeken. Meg mindig jobban jossz ki mint ha minden egyes size() vegigmenne minden elemen...
Masreszrol ha en a standard alapjan irom a szoftvert, fel fogom tetelezni hogy a size() az nem O(n) futasi ideju. Persze ez a szabvany hibaja is, mert nem lenne szabadd ennyire sem megengedonek lennie ezzel kapcsolatban. A "kellene", "lehet", "talan", "esetleg" nem igazan szabvanyba valo szavak.
Gondolom nem arra van tervezve a list, hogy gyakran kérdezgesd a méretét (nem igazán lényeges a felhasználását illetően). Ha ilyet akarsz vele művelni, akkor valszeg rosszul választottál. Van más konténer, amit hatékonyan tudsz indexelni és a méretét kérdezgetni.
Az, hogy egy szabvány ad némi szabadságot az implementációban, vagy nem minden mozzanatot definiál pontosan, az az eddigi tapasztalataim alapján egyáltalán nem ritka (ebben a szakmában). :)
Hahh... Beszarok...
Ez tabnulságos. Az meg pláne, hogy gproof-fal kb 15 perc alatt kiderítetted volna. :)
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o
megprobaltam, megpedig ezen leiras alapjan, igy:
compi@arwen-d32:~$ g++ -o zizi -pg gfx.cpp rects.cpp svgwriter.cpp
compi@arwen-d32:~$ gprof zizi
gmon.out: No such file or directory
es tenyleg nincs, pedig a forditasnak a leirasok szerint a -pg hatasara letre kellett volna hoznia. Na itt adtam fel a gprof-ot es nyultam a probak es hibak modszerehez. Ha esetleg van tipped miert nem jon letre az a nyuves gmon.out, szivesen veszem a segitsegedet.
Egy lépés maradt ki... a futtatás... :)
Tehát fordítás után futtatod, létrehozza a gmon.out-ot, majd gprof...
"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o
Ahhan. Koszi, igy mar letrejon. Mondjuk az egyes stl hivasokban toltott ido pont nincs benne a reportban. Erre ra lehet valahogy venni?
Gondolom azért mer inline-olta az egyes függvényeket. Tiltsd le az inlininget.
-------------------------------------------------
" - Amerikanische Infanterie! Angriff! Angriff! "
Azért nem elmeroggyanásból kifolyólag van így, a doksiban leírják, hogy miért van ez: egy döntés volt természetesen. Sokszor nincs szükség plusz belső méretadatra, ezt nem lehet kivenni, ha nem kell. Szerintem különben még a konzisztencia miatt is van: ne legyen kétszer bekódolva a listába a méret - egyszer a maga lista elemszáma, egyszer egy változóban. Ha meg kell, az ember beleteszi magának, vagy használ más struktúrát.
Persze ilyenkor dühítő, ismerem a szitut. :) Nameg fönti címen is emlegetik, hogy az ISO konstans idejű végrehajtást szorgalmaz, de nem feltétlenül írja elő.
Pont a konzisztencia biztositasa miatt jo adott esetben a redundancia ha nem nagy a koltsege. Hamar kijon ha valami el van szurva a kodban.
Ezzel vitatkoznék... Az, hogy a redundancia jó, ha belefér, az igaz; de csak abban az esetben, ha az ember valóban ellenőrzi is, hogy a független forrásból kijövő adatok passzolnak-e. Ahogy látom, legtöbb helyen azt sem ellenőrzik, amit nagyon kéne, pl. egy malloc kapott-e elég helyet... meg egyéb rendszerhívásokat. Nemhogy még két helyről jövő ugyanolyan adat egyezik-e. A fontos viszont itt inkább az, hogy a konzisztencia nem egyenlő redundanciával.
Szerk: konzisztencia azért van, hogy könnyebb legyen hibamentes algoritmust csinálni. Redundancia nem igazán az algoritmus hibáinak kivédésére van, inkább hardvernyavalyák ellen, szerintem.
oke, pongyolan fogalmaztam. kb azt akartam mondani amit te irtal, bar szerintem nem szukseges a kozisztencia folytonos ellenorzese "eles" verzional, az ojjektum teszteleset viszont nagyban meg tudja konnyiteni, jelen esetben pedig az "eles" kornyezetben jelentos sebessegnovekedest lehetett volna vele elerni. de mind1, ebben az esetben szerencsere megkerulheto volt a size() hivasa.
Nem feltétlen, csak akkor, ha mind a két redundáns adathoz hozzá is férsz. Amúgy meg nem hiszem hogy user oldalról bármikor is valaki átszálazna egy listát, hogy télleg annyi elem van-e benne, mint a list::size() által adott eredmény. Itt meg pont arról volt szó, hogy a _valós_ eredményt kapd, ne egy "hát talán yó" eredményt. Valós méretet akkor ad, ha megszámolja, és az eredményt visszaküldi. Ha a méretváltozó bármely okból nem frissült, akkor te fals eredményeket kapsz.