C++ gcc -msse program lefagy

C++ gcc -msse program lefagy
2016-09-13T17:18:24+02:00
2016-09-22T21:50:44+02:00
2022-12-04T01:25:39+01:00
polyJoe
Sziasztok,
egy összetett c++ (Windows-os) programomat MinGW gcc-vel fordítom (külön 32bites és 64bites kódot ugyanabból a forrásból), a keretrendszer wxWidgets 2.8.12
- 64bites verzióban -march=core2 -mmmx -msse -msse2 -msse3 -mssse3 opciókkal gyönyörűen működik.
-32bites verzióban -march=pentium2 -mmmx gyönyörűen működk
De abban a pillanatban, hogy -msse opciót hozzáteszek és/vagy átállítom -march=pentium3 vagy -march=pentium4 opcióra, a kész program "lefagy". Valójában egy pillanat alatt elhasználja a teljes fizikai memóriát (8GB, miközben a TaskManager szerint csak 2GB-ot foglal), és innentől még az egér se mozdul az erőforráshiány miatt...

Van-e valami megoldás, ötlet, hiszen 32bites verzióban is szeretnék legalább SSE-t használni.
Mutasd a teljes hozzászólást!
Disassembly:

objdump -D filename
Optimalizáció nem számít! Csak ne legyen strippelve... 

Azért látok pár különbséget:
1. wx pentium4, progi pentium3 (P3-ban nincs SSE2! Azt P4-ben vezették be.)
2. progi: -ffast-math (nem biztos, hogy hiba, de különbség)

-ffast-math Sets the options -fno-math-errno, -funsafe-math-optimizations, -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans and -fcx-limited-range. This option causes the preprocessor macro "__FAST_MATH__" to be defined. This option is not turned on by any -O option besides -Ofast since it can result in incorrect output for programs that depend on an exact implementation of IEEE or ISO rules/specifications for math functions. It may, however, yield faster code for programs that do not require the guarantees of these specifications.
3. progi: -fomit-frame-pointer (bár ezt amúgy is megkapod, szóval nem igazi különbség, egyedül az aláhúzott mondat miatt emeltem ki)

-fomit-frame-pointer Don't keep the frame pointer in a register for functions that don't need one. This avoids the instructions to save, set up and restore frame pointers; it also makes an extra register available in many functions. It also makes debugging impossible on some machines. On some machines, such as the VAX, this flag has no effect, because the standard calling sequence automatically handles the frame pointer and nothing is saved by pretending it doesn't exist. The machine-description macro "FRAME_POINTER_REQUIRED" controls whether a target machine supports this flag. The default setting (when not optimizing for size) for 32-bit GNU/Linux x86 and 32-bit Darwin x86 targets is -fomit-frame-pointer. You can configure GCC with the --enable-frame-pointer configure option to change the default. Enabled at levels -O, -O2, -O3, -Os.
----

Érdekességek történhetnek, ha nem stimmel a calling convention.
A wikipedia jelölésével (https://en.wikipedia.org/wiki/X86_calling_conventions):
IA-32, cdecl, GCC: nincs regiszter használat, de

When returning struct/class, the calling code allocates space and passes a pointer to this space via a hidden parameter on the stack. The called function writes the return value to this address.

Stack felülíródás? Ez simán megoldja neked, ha a két oldalon nem ugyanúgy járnak el. Illetve jól el is cseszi a return address-t, rossz helyre ugrik vissza, és elkószál. Bónuszban ott van a gcc, ami olyan optimalizálásokat tud csinálni, hogy csak na.

Csak máshol találtam jó leírás (http://www.agner.org/optimize/calling_conventions.pdf pp 9-10), ahol a stack alignment van részletezve. Az érdekesebb részt bemásolom:

The stack is aligned by 4 in 32-bit Windows.

...

Where at least one function parameter of type __m256 is transferred on the stack, Unix systems (32 and 64 bit) align the parameter by 32 and the called function can rely on the stack being aligned by 32 before the call (i.e. the stack pointer is 32 minus the word size modulo 32 at the function entry). This does not apply if the parameter is transferred in a register.

Various methods for aligning the stack are described in Intel's application note AP 589 "Software Conventions for Streaming SIMD Extensions", "Data Alignment and Programming Issues for the Streaming SIMD Extensions with the Intel® C/C++ Compiler", and "IA-32 Intel ® Architecture Optimization Reference Manual".

Szóval az SSE kicsit ingoványos. Próbáld ugyanazzal a proci beállítással fordítani őket.
Mutasd a teljes hozzászólást!

  • Debuggolj, hogy pontosan hol és mi is történik. Ez így számomra legalábbis túl kevés információ, hogy érdemben segíteni tudjak.
    Mutasd a teljes hozzászólást!
  • Annyit hozzátennék, hogy a dokumentációk alapján nem kell kézzel állítanod az mmx/sse supportokat, amennyiben állítasz 'march'-ot, azzal állítódik az 'mtune', ami magától hozzáadja a támogatott dolgokat.

    lsd:
    3.17.15 Intel 386 and AMD x86-64 Options
    "pentium4, pentium4m
    Intel Pentium4 CPU with MMX, SSE and SSE2 instruction set support."
    Mutasd a teljes hozzászólást!
  • nem kell kézzel állítanod az mmx/sse supportokat

    azért írtam, hogy "és/vagy" - merthogy -march=pentium2 esetén is elromlik, ha melléteszem, hogy -msse

    Debug-olni nagyon nehezen tudom egyrészt mert sok szálon fut, de leginkább mert elfogy a memória.

    A kérdés lényege az, hogy valami programhibát véthettem, ami csak 32bites sse utasításban jön elő? Mert ilyen hibát nem tudok elképzelni.
    Vagy a gcc-ben van hiba (ilyen durva nyilván szintén nem elképzelhető).
    Mutasd a teljes hozzászólást!
  • Hali,

    a fordításnál full verbose compiling. Valamint hozzá teheted:
    --param ggc-min-expand=51
    --param ggc-min-heapsize=40036
    (asszem a MingW-ben is van, lőhetsz rajta, met 8G a géped)

    Egyébbként mikori a MingW-d? Ha a főverzió kisebb mint 6, akkor frissítsd.  A wx verziód is jó, már rég 3 fölött jár. Aktuális verzió: 3.1.0
    Mutasd a teljes hozzászólást!
  • Sikerült valamelyest debug-olni. Ebből a sorból nem megy tovább:

    wxPoint pts[4];
    Így se értem
    Mutasd a teljes hozzászólást!
  • wxPoint (const wxRealPoint &pt)
    Converts the given wxRealPoint (with floating point coordinates) to a wxPoint instance.
    Mutasd a teljes hozzászólást!
  • Jó, de ez nem az, hanem simán egy négy pontból álló tömb.
    Mutasd a teljes hozzászólást!
  • A wxPoint egy két mezőből (x és y) álló varázslat-mentes osztály (nem örököl semmiből, nincsenek benne virtuális metódusok). Önmagában elég ártalmatlan az a tömbdefiníció, még ha le is fut a default konstruktor. Mi van előtte meg utána? Ha teszel elé egy tömbtelen "wxPoint trallala;"-t, akkor az száll el? Ez a futó kód legeleje, vagy addigra már használt valamit sikerrel a könyvtárból? A dll újrafordítása után az alkalmazást is újrafordítottad? Ha import stub-ot használsz, azt is lecserélted az újra?
    Mutasd a teljes hozzászólást!
  • Igen, pont így gondoltam magam is. Jó ötlet: betettem korábbra a trallala-t, de az bizony lefut csont nélkül. És addigra már elég sok minden működik a wxWidget-ből. DLL nincs, import nincs. Szándékosan minden statikusan van belinkelve. Ráadásul a wxWidget P4-re van fordítva, mégis hibátlanul megy, amíg az alkalmazást P2-re fordítom...
    Mutasd a teljes hozzászólást!
  • de ha a rutin elejére "wxPoint trallala[4]" van írva, akkor megáll a szekér...
    Mutasd a teljes hozzászólást!
  • Debuggerral meg tudod nézni a wxPoint előtti 2 műveletet és az értékeket mivel érkezik?
    Mutasd a teljes hozzászólást!
  • Kicseréltem a deklarációt dinamikus tömbre, most működik.
    Ilyen állat nincs!
    Mutasd a teljes hozzászólást!
  • Kíváncsiságból próbáld már ki a legújabb eszközöket használva. (ha megteheted..)
    Mutasd a teljes hozzászólást!
  • Azért az nem olyan egyszerű... Megpróbáltam áttérni a wxWidget 3.x sorozatra, de van pár inkompatibilis vagy deprecated.
    Persze meg fogom próbálni, először talán a MinGW felől kezdve. De ami egyszer működött...
    Mutasd a teljes hozzászólást!
  • Félek, hogy itt valami saját bug van, olyan rejtett hiba a kódomban, ami véletlenül itt jön elő. Nem tudom elhinni, hogy a fordítóban vagy a wxWidget-ben lenne ekkora hiba.
    Mutasd a teljes hozzászólást!
  • Ja hát sok helyen nem lehet megtenni (céges polcyk, stb..).
    Én szerencsére megtehetem, hogy új eszközöket is használjak. Amúgy megéri, volt már olyan problémám, amit egy könyvtár frissítés megoldott..

    Bár nekem az általad tapasztalat hiba továbbra is elég érdekesnek tűnik. Megkellene nézni, hogy mit generál a fordító. Kétlem, hogy egy SSE kapcsoló hozzáadásának bármi köze lenne a default constructor-ok meghívogatásához, vagy 4 objektum stack-en történő helyfoglalásához..
    Mutasd a teljes hozzászólást!
  • Ja hát sok helyen nem lehet megtenni (céges polcyk, stb..).

    A lényeg ezen van, onnantól, hogy meg van hol fagy ki a program, a megfelelő eszközökkel 15 percnél valószínű nem tartana tovább kideríteni konkrétan mi is a gond. Mondjuk a generált kódból is ki lehetne, ahogy írtad is.
    Mutasd a teljes hozzászólást!
  • Kicseréltem a deklarációt dinamikus tömbre, most működik.
    Ilyen állat nincs!

    Van valami ütős kis rekurziód esetleg? Az "egy pillanat alatt elhasználja" megfigyelés is kicsit gyanús volt ebből a szempontból. Ha látsz stack trace-t az elszálláskor, abból ez elég látványosan kiderülhet.

    Nem értek az SSE-hez, de egy keresésben láttam olyat, hogy 128-bites adategységekkel bír dolgozni, és 16 byte-os alignmentet igényel. Miközben 32-bites módban ugye 4-byte az alap igazítás. Szóval ha valami SSE-hez kapcsolódó történik lokális változókkal, akkor hirtelen nagyon megugorhat a verem-fogyasztás.
    Mutasd a teljes hozzászólást!
  • Most ott tartok, hogy első közelítésben újrafordítottam az egész wxWidget-et, hátha... de nem.
    Rekurzióm nincs. De jók, amiket mondasz, valahol a verem-használat körül lehet a gond.
    Mutasd a teljes hozzászólást!
  • gdb-ben válts át assembly módba, és utasításonként nézd meg, hogy mit csinál. Ha egy "apró" művelet van C++ban, az attól még lefordulhat nagyon sok assembly utasításra.
    Mutasd a teljes hozzászólást!
  • Nos, azért tűntem el ennyi időre, mert hosszadalmas upgrade volt.
    Frissítettem a MinGW-t, a GCC-t, a wxWidget-et. Persze ugyanazon opciókkal nem fordul, könyvtárak, környezeti változók, inkompatibilis kódsorok, átalakítás unicode-ra stb. Huh, na ezért nem szeretek frissíteni!
    De végre minden összeállt, minden új. Olyan boldogan mondanám, hogy ezáltal a hiba is megszűnt... de nem a jelenség ugyanaz: -march=pentium2 fordítással hibátlanul fut, -march=pentium3 fordítással leáll. És a 64bites kód -march=core2 opcióval fut hibátlanul (de az gcc-x64).
    Ettől függetlenül nyitva hagyom a témát, mert most hosszadalmas hibakeresés jön a kódomban, a történet úgy lesz kerek, ha leírom majd a megfejtést (hátha más is belefut ilyenbe a jövőben).
    Egyre valószínűbbnek tartom, hogy valami veremben levő változó inicializálását rontom el.

    Addig is mindenkinek köszönöm a hasznos tanácsokat!
    Mutasd a teljes hozzászólást!
  • Ilyenkor segíthet az, hogy egyre több rész kikommentezésével egyre kisebb területre korlátozod a hiba lehetséges helyét. (A gyorsabb haladás érdekében érdemes mindig kb. felezni a megmaradó kódot.) Ezzel vagy megtalálod a hibát, vagy lesz egy olyan minimális, de a hibát még mindig produkáló kódod, amit már beküldetsz ide, és talán lesz valaki, aki megtalálja a hibát benne. (Általában szokott lenni!!??)
    Mutasd a teljes hozzászólást!
  • Köszi, ennyit én is értek a hibakereséshez :) Lásd fentebb, egy sorra tudtam a hibát korlátozni:

    wxPoint pts[4];
    Kérlek írd meg, milyen hibát találsz benne!
    Mutasd a teljes hozzászólást!
  • wx-et is forrásból fordítod? Ha igen, akkor ugyanazokkal a kapcsolókkal? Ha nem, akkor esetleg az lehet még, hogy a lib, amit hozzáfordítasz, más calling convention-t használ, mint a hívó. Ez PC-n SSE mellett tud problémát okozni. Ezzel kanyarodnék vissza az assembly debug-os hozzászólásomhoz, mert ott látszana az ilyen. Az is, ha esetleg valami rossz címre ugrik. De most a calling convention lenne az első dolog, amit ellenőriznék. Plusz kérdés: melyik C++ szabványt használod? Egy teljes g++ kapcsolólistát mellékelsz?

    szerk: Annak az egy szem függvénynek a disassembly-jét átküldöd? Új tippem van: ha SSE-vel inicializálja a 4 elemű tömbödet (ami összesen 4*2 int = 32 byte, ha 4==sizeof(int), és azt már "megéri" SSE-vel inicializálni), akkor esetleg hülyét kaphat attól, ha a stack változód nem alignolt. SSE az 128 bit alignment-et követel meg, ami több, mint a normál stack alignment. Ha ez a helyzet, akkor nincs mit tenni, compiler bug. Esetleg tudsz workaround-ot csinálni rá.

    szerk2: Printeld ki a tömböd címét. Mit ír ki? (ASLR miatt használhatatlan a cím, de az alignment legalább látszódna.) Ok sok memória használatra: végtelen rekurzió mondjuk egy szarul generált throw-ban.
    Mutasd a teljes hozzászólást!
  • Ilyenkor jön az a rész, hogy létrehozol olyan mintakódot amiben ugyan ez hibát okoz, és közzé teszed. Az pedig, hogy ennél a sornál siklik ki a programod, nem feltétlen jelenti azt, hogy ez a hiba forrása.
    Mutasd a teljes hozzászólást!
  • Húú, lehet, hogy a calling convention lesz a kulcs?
    Ugyanis most oda jutottam, hogy vándorol a hiba. Ha egy helyen kikerülöm, akkor máshol jön elő. Pont olyan, mintha véletlenszerűen belerondítana a stack-be.
    Ha -O3 helyett -O2 (kevesebb optimalizálás) opcióval fordítok, akkor működik.
    A disassemblyvel egyelőre bajom van, mert a debug módú wxWidgets nem ugyanúgy viselkedik. Próbálom a release verziót debug módú programmal, de zűrös, és a disassembly nem működik valamiért. Erre még rá kell jönnöm.

    Igen, a wxWidget-et is magam fordítom:

    mingw32-make -j CFLAGS="-m32 -march=pentium4 -mtune=pentium4 -msse2" CPPFLAGS="-m32 -march=pentium4 -mtune=pentium4 -msse2" CXXFLAGS="-m32 -std=gnu++11 -fpermissive -march=pentium4 -mtune=pentium4 -msse2" LDFLAGS="-m32" -f makefile.gcc BUILD=release SHARED=0 DEBUG=0 UNICODE=1 MONOLITHIC=0 RUNTIME_LIBS=static %2
    de gondolom nem ez kellene, hanem inkább a makefile által generált parancs, ilyen:

    g++ -c -o gcc_mswu\corelib_msw_listbox.o -O2 -mthreads -DHAVE_W32API_H -D__WXMSW__ -DNDEBUG -D_UNICODE -I..\..\lib\gcc_lib\mswu -I..\..\include -W -Wall -DWXBUILDING -I..\..\src\tiff\libtiff -I..\..\src\jpeg -I..\..\src\png -I..\..\src\zlib -I..\..\src\regex -I..\..\src\expat\lib -DwxUSE_BASE=0 -Wno-ctor-dtor-privacy -m32 -march=pentium4 -mtune=pentium4 -msse2 -m32 -std=gnu++11 -fpermissive -march=pentium4 -mtune=pentium4 -msse2 -MTgcc_mswu\corelib_msw_listbox.o -MFgcc_mswu\corelib_msw_listbox.o.d -MD -MP ../../src/msw/listbox.cpp
    A programomat pedig így fordítom (ez egy object fordítása a Code:Block ablakából):

    g++.exe -pipe -mthreads -ffast-math -D__GNUWIN32__ -D__WXMSW__ -D__WINDOWS_ASIO__ -D__WINDOWS_MM__ -D__WINDOWS_DS__ -march=pentium3 -fomit-frame-pointer -fexpensive-optimizations -O2 -m32 -ftree-vectorize -DUNICODE -DwxUSE_UNICODE=1 -std=gnu++11 -IC:\wxWidgets-3.0.2\include -Idirectx\Include -IC:\wxWidgets-3.0.2\include -Ic:\wxWidgets-3.0.2
    Bevallom, nem látok hibát se lényegi eltérést...
    Mutasd a teljes hozzászólást!
  • Disassembly:

    objdump -D filename
    Optimalizáció nem számít! Csak ne legyen strippelve... 

    Azért látok pár különbséget:
    1. wx pentium4, progi pentium3 (P3-ban nincs SSE2! Azt P4-ben vezették be.)
    2. progi: -ffast-math (nem biztos, hogy hiba, de különbség)

    -ffast-math Sets the options -fno-math-errno, -funsafe-math-optimizations, -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans and -fcx-limited-range. This option causes the preprocessor macro "__FAST_MATH__" to be defined. This option is not turned on by any -O option besides -Ofast since it can result in incorrect output for programs that depend on an exact implementation of IEEE or ISO rules/specifications for math functions. It may, however, yield faster code for programs that do not require the guarantees of these specifications.
    3. progi: -fomit-frame-pointer (bár ezt amúgy is megkapod, szóval nem igazi különbség, egyedül az aláhúzott mondat miatt emeltem ki)

    -fomit-frame-pointer Don't keep the frame pointer in a register for functions that don't need one. This avoids the instructions to save, set up and restore frame pointers; it also makes an extra register available in many functions. It also makes debugging impossible on some machines. On some machines, such as the VAX, this flag has no effect, because the standard calling sequence automatically handles the frame pointer and nothing is saved by pretending it doesn't exist. The machine-description macro "FRAME_POINTER_REQUIRED" controls whether a target machine supports this flag. The default setting (when not optimizing for size) for 32-bit GNU/Linux x86 and 32-bit Darwin x86 targets is -fomit-frame-pointer. You can configure GCC with the --enable-frame-pointer configure option to change the default. Enabled at levels -O, -O2, -O3, -Os.
    ----

    Érdekességek történhetnek, ha nem stimmel a calling convention.
    A wikipedia jelölésével (https://en.wikipedia.org/wiki/X86_calling_conventions):
    IA-32, cdecl, GCC: nincs regiszter használat, de

    When returning struct/class, the calling code allocates space and passes a pointer to this space via a hidden parameter on the stack. The called function writes the return value to this address.

    Stack felülíródás? Ez simán megoldja neked, ha a két oldalon nem ugyanúgy járnak el. Illetve jól el is cseszi a return address-t, rossz helyre ugrik vissza, és elkószál. Bónuszban ott van a gcc, ami olyan optimalizálásokat tud csinálni, hogy csak na.

    Csak máshol találtam jó leírás (http://www.agner.org/optimize/calling_conventions.pdf pp 9-10), ahol a stack alignment van részletezve. Az érdekesebb részt bemásolom:

    The stack is aligned by 4 in 32-bit Windows.

    ...

    Where at least one function parameter of type __m256 is transferred on the stack, Unix systems (32 and 64 bit) align the parameter by 32 and the called function can rely on the stack being aligned by 32 before the call (i.e. the stack pointer is 32 minus the word size modulo 32 at the function entry). This does not apply if the parameter is transferred in a register.

    Various methods for aligning the stack are described in Intel's application note AP 589 "Software Conventions for Streaming SIMD Extensions", "Data Alignment and Programming Issues for the Streaming SIMD Extensions with the Intel® C/C++ Compiler", and "IA-32 Intel ® Architecture Optimization Reference Manual".

    Szóval az SSE kicsit ingoványos. Próbáld ugyanazzal a proci beállítással fordítani őket.
    Mutasd a teljes hozzászólást!
  • Akkor nem ment át az üzenet. Te magad is rájöttél, hogy a hiba nem ebben a sorban van, hanem máshol.

    Azzal, hogy kikommentezel/kitörölsz más programrészeket és egyre kisebb kódot futtatsz mindaddig, amíg a hiba fennáll, azt éred el, hogy tudni fogod, abban a lépésben, ami után megszűnt a hiba, pont a hibás sort is kitörölted. Tehát meg kell nézni, mit töröltél ki utolsónak és azon belül lesz a hiba. Ezt így folytatva biztos meglesz a tényleges ok.

    Amúgy még mindig nem látom, a minimális kódodat hova küldted be. Anélkül érdemben senki nem fog tudni segíteni, csak random találgatások lesznek.
    Mutasd a teljes hozzászólást!
  • Ja, egy minimalist test case nem artana...
    Mondjuk 1 het alatt en mar legalabb 77x nyultam volna egy external assemblerhez, az mindig azt csinalja, amit mondok :D

    Ez az align dolog meg:
    A -fast_math-nak nem kell 16byte align, mert az movd/movq-val dolgozik.
    Ha meg 128bit sse utasitasok vannak, a compiler csak tudja már hogy mit alignolt es mit nem?!
    A hiba pedig ha jol ertem nem egy read/write error (ami az alignbol kovetkezne), hanem out of memory error. <- Ezt tenyleg nem konnyu debuggolni, ehhez jonne jol a minimal testcase nyirbalasa, ahogy hosza is irja.
    Mutasd a teljes hozzászólást!
Tetszett amit olvastál? Szeretnél a jövőben is értesülni a hasonló érdekességekről?
abcd