Seite 1 von 4

Re: Is it just me, or …

Verfasst: 21.05.2010, 14:55
von Krishty
Habe die beiden nicht mehr installiert … probier es aus und poste hier :)

Re: Is it just me, or …

Verfasst: 21.05.2010, 14:58
von jgl
Würde ich gerne machen, aber wie weis ich nicht ;)

Re: Is it just me, or …

Verfasst: 21.05.2010, 15:02
von Krishty

Code: Alles auswählen

// Compile with /Ox /Ob2, link with /LTCG
#include <iostream>

struct Vector {
    float A, B, C, D;

    Vector(float A, float B, float C, float D)
        : A(A), B(B), C(C), D(D) { }

    Vector operator + (Vector const & Summand) {
        return Vector(A + Summand.A, B + Summand.B, C + Summand.C, D + Summand.D);
    }

    ~Vector() { }
};

int main() {

    float A, B, C, D;
    ::std::cin >> A >> B >> C >> D;
    Vector const Result = Vector(A, B, C, D) + Vector(0.0f, 1.0f, 2.0f, 3.0f);
    ::std::cout << Result.A << Result.B << Result.C << Result.D;

    return 0;
}
Release-Build (Full Optimization, Whole Program Optimization, Use Link-Time Code Generation), mit Strg+F10 in die Zeile mit dem ::std::cin springen, Rechtsklick -> View Disassembly. Wenn ein call Vector::operator + drinsteht, ist der Bug mit hoher Wahrscheinlichkeit auch dort vorhanden. Dann Debugging abbrechen, den Destruktor auskommentieren, neu builden, wieder in die Zeile springen, wieder Disassembly anschauen. Wenn die Addition komplett geinlined wurde und der Code insgesamt kürzer ist, haben wir einen Treffer. Oder, falls du mit Disassembly nicht viel anfangen kannst, den Assembler-Code beide Male rauskopieren und hier posten.

Re: Is it just me, or …

Verfasst: 21.05.2010, 15:18
von jgl
Okay, also so wie Du beschrieben hast (alle Compiler- und Linkereinstellung so gemacht), Code kopiert.

Also:
1) VS2005: dort taucht ein "call Vector::operator+ (401000h) " auf.
Ich poste mal das ganzen Disassembly

Code: Alles auswählen

int main() {
00401030  push        ebp  
00401031  mov         ebp,esp 
00401033  and         esp,0FFFFFFF8h 
00401036  sub         esp,40h 

    float A, B, C, D;
    ::std::cin >> A >> B >> C >> D;
00401039  lea         eax,[esp+0Ch] 
0040103D  push        eax  
0040103E  lea         ecx,[esp+0Ch] 
00401042  push        ecx  
00401043  mov         ecx,dword ptr [__imp_std::cin (402040h)] 
00401049  lea         edx,[esp+0Ch] 
0040104D  push        edx  
0040104E  lea         eax,[esp+0Ch] 
00401052  push        eax  
00401053  call        dword ptr [__imp_std::basic_istream<char,std::char_traits<char> >::operator>> (402044h)] 
00401059  mov         ecx,eax 
0040105B  call        dword ptr [__imp_std::basic_istream<char,std::char_traits<char> >::operator>> (402044h)] 
00401061  mov         ecx,eax 
00401063  call        dword ptr [__imp_std::basic_istream<char,std::char_traits<char> >::operator>> (402044h)] 
00401069  mov         ecx,eax 
0040106B  call        dword ptr [__imp_std::basic_istream<char,std::char_traits<char> >::operator>> (402044h)] 
    Vector const Result = Vector(A, B, C, D) + Vector(0.0f, 1.0f, 2.0f, 3.0f);
00401071  fldz             
00401073  fstp        dword ptr [esp+10h] 
00401077  lea         ecx,[esp+10h] 
0040107B  fld1             
0040107D  lea         eax,[esp+30h] 
00401081  fstp        dword ptr [esp+14h] 
00401085  lea         edx,[esp+20h] 
00401089  fld         dword ptr [__real@40000000 (402118h)] 
0040108F  fstp        dword ptr [esp+18h] 
00401093  fld         dword ptr [__real@40400000 (402114h)] 
00401099  fstp        dword ptr [esp+1Ch] 
0040109D  fld         dword ptr [esp] 
004010A0  fstp        dword ptr [esp+20h] 
004010A4  fld         dword ptr [esp+4] 
004010A8  fstp        dword ptr [esp+24h] 
004010AC  fld         dword ptr [esp+8] 
004010B0  fstp        dword ptr [esp+28h] 
004010B4  fld         dword ptr [esp+0Ch] 
004010B8  fstp        dword ptr [esp+2Ch] 
004010BC  call        Vector::operator+ (401000h) 
    ::std::cout << Result.A << Result.B << Result.C << Result.D;
004010C1  fld         dword ptr [esp+3Ch] 
004010C5  mov         ecx,dword ptr [__imp_std::cout (40203Ch)] 
004010CB  sub         esp,10h 
004010CE  fstp        dword ptr [esp+0Ch] 
004010D2  fld         dword ptr [esp+48h] 
004010D6  fstp        dword ptr [esp+8] 
004010DA  fld         dword ptr [esp+44h] 
004010DE  fstp        dword ptr [esp+4] 
004010E2  fld         dword ptr [esp+40h] 
004010E6  fstp        dword ptr [esp] 
004010E9  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (402038h)] 
004010EF  mov         ecx,eax 
004010F1  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (402038h)] 
004010F7  mov         ecx,eax 
004010F9  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (402038h)] 
004010FF  mov         ecx,eax 
00401101  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (402038h)] 

    return 0;
00401107  xor         eax,eax 
}
1) VS2008: dort taucht ein "call Vector::operator+ (401000h) " auf.
Ich poste mal wieden das ganzen Disassembly

Code: Alles auswählen

   
 int main() {
01131030  push        ebp  
01131031  mov         ebp,esp 
01131033  and         esp,0FFFFFFF8h 
01131036  sub         esp,40h 

        float A, B, C, D;
        ::std::cin >> A >> B >> C >> D;
01131039  lea         eax,[esp+0Ch] 
0113103D  push        eax  
0113103E  lea         ecx,[esp+0Ch] 
01131042  push        ecx  
01131043  mov         ecx,dword ptr [__imp_std::cin (1132040h)] 
01131049  lea         edx,[esp+0Ch] 
0113104D  push        edx  
0113104E  lea         eax,[esp+0Ch] 
01131052  push        eax  
01131053  call        dword ptr [__imp_std::basic_istream<char,std::char_traits<char> >::operator>> (1132038h)] 
01131059  mov         ecx,eax 
0113105B  call        dword ptr [__imp_std::basic_istream<char,std::char_traits<char> >::operator>> (1132038h)] 
01131061  mov         ecx,eax 
01131063  call        dword ptr [__imp_std::basic_istream<char,std::char_traits<char> >::operator>> (1132038h)] 
01131069  mov         ecx,eax 
0113106B  call        dword ptr [__imp_std::basic_istream<char,std::char_traits<char> >::operator>> (1132038h)] 
        Vector const Result = Vector(A, B, C, D) + Vector(0.0f, 1.0f, 2.0f, 3.0f);
01131071  fldz             
01131073  fstp        dword ptr [esp+10h] 
01131077  lea         ecx,[esp+10h] 
0113107B  fld1             
0113107D  lea         eax,[esp+30h] 
01131081  fstp        dword ptr [esp+14h] 
01131085  lea         edx,[esp+20h] 
01131089  fld         dword ptr [__real@40000000 (1132118h)] 
0113108F  fstp        dword ptr [esp+18h] 
01131093  fld         dword ptr [__real@40400000 (1132114h)] 
01131099  fstp        dword ptr [esp+1Ch] 
0113109D  fld         dword ptr [esp] 
011310A0  fstp        dword ptr [esp+20h] 
011310A4  fld         dword ptr [esp+4] 
011310A8  fstp        dword ptr [esp+24h] 
011310AC  fld         dword ptr [esp+8] 
011310B0  fstp        dword ptr [esp+28h] 
011310B4  fld         dword ptr [esp+0Ch] 
011310B8  fstp        dword ptr [esp+2Ch] 
011310BC  call        Vector::operator+ (1131000h) 
        ::std::cout << Result.A << Result.B << Result.C << Result.D;
011310C1  fld         dword ptr [esp+3Ch] 
011310C5  mov         ecx,dword ptr [__imp_std::cout (1132044h)] 
011310CB  sub         esp,10h 
011310CE  fstp        dword ptr [esp+0Ch] 
011310D2  fld         dword ptr [esp+48h] 
011310D6  fstp        dword ptr [esp+8] 
011310DA  fld         dword ptr [esp+44h] 
011310DE  fstp        dword ptr [esp+4] 
011310E2  fld         dword ptr [esp+40h] 
011310E6  fstp        dword ptr [esp] 
011310E9  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (113203Ch)] 
011310EF  mov         ecx,eax 
011310F1  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (113203Ch)] 
011310F7  mov         ecx,eax 
011310F9  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (113203Ch)] 
011310FF  mov         ecx,eax 
01131101  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (113203Ch)] 

        return 0;
01131107  xor         eax,eax 
    }
Und nun habe ich den leeren Destructor rausgeschmissen und es steht auch kein " call Vector::operator+" mehr.
Kommt mir auch kürzer vor :)
Also demnach existiert dieses Problem schon seit VS2005...

Re: Is it just me, or …

Verfasst: 21.05.2010, 15:23
von Krishty
Super. Damit wäre das reproduziert :) Falls du bei MSDN Connect bist, kannst du das Ticket ja bestätigen/uppen.

Re: Is it just me, or …

Verfasst: 21.05.2010, 15:26
von jgl
Destructor nun auskommentiert.

Disassembly VS 2005:

Code: Alles auswählen

int main() {
00401000  sub         esp,20h 

    float A, B, C, D;
    ::std::cin >> A >> B >> C >> D;
00401003  lea         eax,[esp+0Ch] 
00401007  push        eax  
00401008  lea         ecx,[esp+0Ch] 
0040100C  push        ecx  
0040100D  mov         ecx,dword ptr [__imp_std::cin (402040h)] 
00401013  lea         edx,[esp+0Ch] 
00401017  push        edx  
00401018  lea         eax,[esp+0Ch] 
0040101C  push        eax  
0040101D  call        dword ptr [__imp_std::basic_istream<char,std::char_traits<char> >::operator>> (402044h)] 
00401023  mov         ecx,eax 
00401025  call        dword ptr [__imp_std::basic_istream<char,std::char_traits<char> >::operator>> (402044h)] 
0040102B  mov         ecx,eax 
0040102D  call        dword ptr [__imp_std::basic_istream<char,std::char_traits<char> >::operator>> (402044h)] 
00401033  mov         ecx,eax 
00401035  call        dword ptr [__imp_std::basic_istream<char,std::char_traits<char> >::operator>> (402044h)] 
    Vector const Result = Vector(A, B, C, D) + Vector(0.0f, 1.0f, 2.0f, 3.0f);
0040103B  fld         dword ptr [esp] 
0040103E  fadd        qword ptr [__real@0000000000000000 (402130h)] 
    ::std::cout << Result.A << Result.B << Result.C << Result.D;
00401044  mov         ecx,dword ptr [__imp_std::cout (40203Ch)] 
0040104A  sub         esp,10h 
0040104D  fstp        dword ptr [esp+20h] 
00401051  fld         dword ptr [esp+14h] 
00401055  fadd        qword ptr [__real@3ff0000000000000 (402128h)] 
0040105B  fstp        dword ptr [esp+24h] 
0040105F  fld         dword ptr [esp+18h] 
00401063  fadd        qword ptr [__real@4000000000000000 (402120h)] 
00401069  fstp        dword ptr [esp+28h] 
0040106D  fld         dword ptr [esp+1Ch] 
00401071  fadd        qword ptr [__real@4008000000000000 (402118h)] 
00401077  fstp        dword ptr [esp+2Ch] 
0040107B  fld         dword ptr [esp+2Ch] 
0040107F  fstp        dword ptr [esp+0Ch] 
00401083  fld         dword ptr [esp+28h] 
00401087  fstp        dword ptr [esp+8] 
0040108B  fld         dword ptr [esp+24h] 
0040108F  fstp        dword ptr [esp+4] 
00401093  fld         dword ptr [esp+20h] 
00401097  fstp        dword ptr [esp] 
0040109A  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (402038h)] 
004010A0  mov         ecx,eax 
004010A2  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (402038h)] 
004010A8  mov         ecx,eax 
004010AA  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (402038h)] 
004010B0  mov         ecx,eax 
004010B2  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (402038h)] 

    return 0;
004010B8  xor         eax,eax 
}
Disassembly VS 2008:

Code: Alles auswählen

    int main() {
00181000  sub         esp,20h 

        float A, B, C, D;
        ::std::cin >> A >> B >> C >> D;
00181003  lea         eax,[esp+0Ch] 
00181007  push        eax  
00181008  lea         ecx,[esp+0Ch] 
0018100C  push        ecx  
0018100D  mov         ecx,dword ptr [__imp_std::cin (182040h)] 
00181013  lea         edx,[esp+0Ch] 
00181017  push        edx  
00181018  lea         eax,[esp+0Ch] 
0018101C  push        eax  
0018101D  call        dword ptr [__imp_std::basic_istream<char,std::char_traits<char> >::operator>> (182038h)] 
00181023  mov         ecx,eax 
00181025  call        dword ptr [__imp_std::basic_istream<char,std::char_traits<char> >::operator>> (182038h)] 
0018102B  mov         ecx,eax 
0018102D  call        dword ptr [__imp_std::basic_istream<char,std::char_traits<char> >::operator>> (182038h)] 
00181033  mov         ecx,eax 
00181035  call        dword ptr [__imp_std::basic_istream<char,std::char_traits<char> >::operator>> (182038h)] 
        Vector const Result = Vector(A, B, C, D) + Vector(0.0f, 1.0f, 2.0f, 3.0f);
0018103B  fld         dword ptr [esp] 
0018103E  fadd        qword ptr [__real@0000000000000000 (182130h)] 
        ::std::cout << Result.A << Result.B << Result.C << Result.D;
00181044  mov         ecx,dword ptr [__imp_std::cout (182044h)] 
0018104A  sub         esp,10h 
0018104D  fstp        dword ptr [esp+20h] 
00181051  fld         dword ptr [esp+14h] 
00181055  fadd        qword ptr [__real@3ff0000000000000 (182128h)] 
0018105B  fstp        dword ptr [esp+24h] 
0018105F  fld         dword ptr [esp+18h] 
00181063  fadd        qword ptr [__real@4000000000000000 (182120h)] 
00181069  fstp        dword ptr [esp+28h] 
0018106D  fld         dword ptr [esp+1Ch] 
00181071  fadd        qword ptr [__real@4008000000000000 (182118h)] 
00181077  fstp        dword ptr [esp+2Ch] 
0018107B  fld         dword ptr [esp+2Ch] 
0018107F  fstp        dword ptr [esp+0Ch] 
00181083  fld         dword ptr [esp+28h] 
00181087  fstp        dword ptr [esp+8] 
0018108B  fld         dword ptr [esp+24h] 
0018108F  fstp        dword ptr [esp+4] 
00181093  fld         dword ptr [esp+20h] 
00181097  fstp        dword ptr [esp] 
0018109A  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (18203Ch)] 
001810A0  mov         ecx,eax 
001810A2  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (18203Ch)] 
001810A8  mov         ecx,eax 
001810AA  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (18203Ch)] 
001810B0  mov         ecx,eax 
001810B2  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (18203Ch)] 

        return 0;
001810B8  xor         eax,eax 
    }

Re: Is it just me, or …

Verfasst: 27.05.2010, 19:51
von Krishty
Mal ein kleines Update:

Bei MS konnte der Bug zuerst nicht reproduziert werden. Ich musste nochmal ran und habe dann herausgefunden, dass Exception-Handling aktiviert sein muss, damit das Verhalten auftritt …

… weiter vorne im Thread habe ich mich ja schon darüber aufgeregt, dass Funktionen, die try-Blöcke enthalten, nicht geinlined werden können. Aus dieser Zeit – besser gesagt aus der Beschreibung der Warnung C4717 – hatte ich noch im Hinterkopf, dass Funktionen nicht geinlined werden können, wenn ihre Rückgabewerte unwindable sind – also im Fall einer Exception ein Destruktor aufgerufen werden müsste.

Ich schätze die Situation so ein, dass der Compiler in dem Code-Beispiel Vector als unwindable klassifiziert, sobald man einen Destruktor – wenn auch nur einen leeren – definiert, und es deshalb ablehnt, Vector::operator + () zu inlinen.

Das alles wäre nicht merkwürdig, wenn der Compiler nicht normalerweise großartige Arbeit bei solchen Klassifizierungen leisten würde. Dass all die Mechanismen, die sonst bestimmen ob eine Funktion wegoptimiert werden kann und ob sie Exception-Handling benötigt, bei einem manuell definierten Destruktor versagen ist vielleicht keinen Bug-Report mehr wert, aber zumindest einen Verbesserungsvorschlag. Jetzt warte ich aber erstmal ab, was das VC-Team dazu sagt, vielleicht bin ich ja auch auf dem Holzweg.

Re: Is it just me, or …

Verfasst: 05.06.2010, 16:09
von eXile
Hier mal wieder etwas aus der Abteilung "merkwürdiges Verhalten": http://ompf.org/forum/viewtopic.php?t=1744&p=19145

Und wir sehen: Ohne __forceinline wird der Code geinlined und die überflüssigen Konstruktoren werden verworfen …

Re: Is it just me, or …

Verfasst: 05.06.2010, 17:46
von Krishty
Interessant … was es nicht alles gibt. Ich schätze, der Initializer des Arrays wird nur aus bestimmten Stellen rausoptimiert … ich werde direkt mal prüfen, ob ich irgendwo uninitialisierte Arrays habe.

Re: Is it just me, or …

Verfasst: 12.06.2010, 22:57
von Krishty
Hmmm, der Compiler scheint auch nicht daraufhin zu optimieren, dass durch Datentypgrenzen bestimmte Vergleiche konstant sind:

Code: Alles auswählen

bool CheckCharacter(char x) { // default char signed
    return (48 <= x) && (127 >= x);
}
Hier wird der Compiler beide Vergleiche durchführen, obwohl der zweite Vergleich durch das char-Intervall von [-128, 127] immer true liefert.

Immer, wenn eine Bereichsgrenze auf 0, 127, 32767 oder 2^31-1 liegt, kann man durch den richtigen Datentypen einen Vergleich sparen. Leider muss man das scheinbar manuell machen. Oder gibt es einen guten Grund, warum der Compiler das nicht optimiert?

Re: Is it just me, or …

Verfasst: 13.06.2010, 09:57
von TGGC
Krishty hat geschrieben:Hmmm, der Compiler scheint auch nicht daraufhin zu optimieren, dass durch Datentypgrenzen bestimmte Vergleiche konstant sind:

Code: Alles auswählen

bool CheckCharacter(char x) { // default char signed
    return (48 <= x) && (127 >= x);
}
Hier wird der Compiler beide Vergleiche durchführen, obwohl der zweite Vergleich durch das char-Intervall von [-128, 127] immer true liefert.

Immer, wenn eine Bereichsgrenze auf 0, 127, 32767 oder 2^31-1 liegt, kann man durch den richtigen Datentypen einen Vergleich sparen. Leider muss man das scheinbar manuell machen. Oder gibt es einen guten Grund, warum der Compiler das nicht optimiert?
Nein, der zweite Vergleich ist nicht immer wahr, sondern nur wenn x >= 127 ist. f'`8k


Gruß, TGGC (der kostenlose DMC Download)

Re: Is it just me, or …

Verfasst: 13.06.2010, 10:17
von Alexander Kornrumpf
TGGC: Du hast die richtung des Vergleichs umgedreht.

Krishty: Mal ne ganz platte Vermutung: Es kommt extrem selten vor dass jemand einen integralwert mit einer konstanten an der Grenze des Wertebereichs vergleicht und lohnt sich daher nicht.

Re: Is it just me, or …

Verfasst: 13.06.2010, 10:42
von Schrompf
Nein, TGGC hat schon recht: "127 >= x" bedeutet eben "größer oder gleich". Und char kann nunmal einen Wert von 127 annehmen. Die Bedingung ist also nicht zwangsweise falsch.

Re: Is it just me, or …

Verfasst: 13.06.2010, 10:52
von Alexander Kornrumpf
Thomas:
Krishty: 127 >= x oder auch x <=127

TGGC: x >= 127

ist schon ein Unterschied.

P.S. Genau deswegen ist konstanten nach vorne schreiben unituitiv.
P.P.S. Was soll überhaupt das dämliche = dort? 128 > x hätte es doch auch getan. Vielleicht kapiert der compiler das sogar besser.

Re: Is it just me, or …

Verfasst: 13.06.2010, 11:28
von Schrompf
Oh stimmt, hab mich von der verworrenen Schreibweise verwirren lassen.

Re: Is it just me, or …

Verfasst: 13.06.2010, 11:58
von Jörg
Vielleicht sollte man aufhoeren, MS VC als DEN Compiler zu sehen ;) Selbst der betagte gcc 4.3.3 bekommt es gebacken.

Re: Is it just me, or …

Verfasst: 13.06.2010, 12:06
von Schrompf
Och naja... VC++ mag nicht DER Compiler sein, aber solange Visual Studio DEN Debugger hat, wird es kaum eine Diskussion geben :-)

Ich verfolge die gelegentlich auftauchenden Diskussionen über Compilerqualitäten... und soweit ich das beurteilen kann, produziert der GCC wirklich den besseren Code, teilweise drastisch schneller. Das tut allerdings auch der Intel-Compiler. Der AMD-Compiler taucht aus mir unerfindlichen Gründen bislang in keiner Diskussion auf. Der VC-Compiler hat allerdings einen großen Vorteil: er kompiliert schneller. Nach meinen subjektiven Einschätzungen durchaus Faktor 3 bis 5 schneller als der GCC.

Re: Is it just me, or …

Verfasst: 13.06.2010, 13:22
von Krishty
Alexander Kornrumpf hat geschrieben:P.P.S. Was soll überhaupt das dämliche = dort? 128 > x hätte es doch auch getan. Vielleicht kapiert der compiler das sogar besser.
In dem Augenblick, in dem ich 128 hinschriebe, würden beide Operanden als signed short behandelt, weil 128 zu groß für char ist.
Jörg hat geschrieben:Vielleicht sollte man aufhoeren, MS VC als DEN Compiler zu sehen ;) Selbst der betagte gcc 4.3.3 bekommt es gebacken.
Ich habe vor ein paar Monaten mal versucht, auf GCC umzusteigen … und so viel besser als VC ist der auch nicht. Den ersten Bug hatte ich – kein Witz – in der ersten Programmzeile: GCC #defined __cplusplus einfach zu 1 statt zum Datum des Sprachstandards. Von da an ging es dann weiter mit __builtins, die nicht existieren obwohl das komplette Internet das Gegenteil behauptet, einer Standardbibliothek wo die Hälfte (so ziemlich alles mit wchar_t) noch nicht implementiert ist und endet dann mit dem herrlichen online-Support, der schlicht und einfach nicht existiert (darüber, ob sie __cplusplus nun endlich standardkonform machen, flamen sie seit sieben Jahren). Irgendwie ist es da bequemer, von Zeit zu Zeit das VC-Kompilat gegenzutesten und hier und da von Hand zu optimieren. Und was Schrompf sagt kommt auch noch dazu.

Nichtsdestotrotz benutze ich GCC einmal im Monat, um meinen Code zu parsen – fehlerhaften Template-Code oder nicht standardkonforme Syntaxkonstrukte findet er bedeutend häufiger als VC, das z.B. die Spezialisierung von Templates im class-Scope erlaubt. Wichtig insbesondere seit ich in VC nicht mehr die Language Extensions deaktivieren will, weil ich dann die C++0x-Features einbüßen würde.

Re: Is it just me, or …

Verfasst: 13.06.2010, 14:44
von Jörg
So war's nicht gemeint, ging mir eher darum, dass man solche Dinge ja schnell mit einem "Zweitcompiler" beliebiger Wahl gegentesten kann. Wollte keinesfalls den Thread in einem Flamewar münden lassen. Und offensichtliche Unzulänglichkeiten sollte man natuerlich in einem Feature-Request formulieren, egal fuer welchen Compiler.

Re: Is it just me, or …

Verfasst: 13.06.2010, 16:00
von Krishty
Alles klar. Während der Stunde, die ich jetzt brauche um MinGW C++0x-tauglich zu machen, könnt ihr ja mal darüber nachdenken, warum VC ein Placement-new durch Move-Constructor mit einem nullptr-Check versieht:

Code: Alles auswählen

new (&Array[Index]) int(::std::move(Array[Index-1]));

xor         ebx, ebx
lea         eax,[edx+ecx*4]  
cmp         eax,ebx  
je          0F21260h
mov         edi,dword ptr [eax-4]  
mov         dword ptr [eax],edi
dec         ecx // 0F21260h
Kurz: Bevor die int verschoben wird, wird getestet, ob das Ziel nullptr ist. Falls das der Fall ist, wird die Bewegung einfach übersprungen (es dient also nicht dazu, einen Fehler zu melden oder so). Weder die Implementierung vom Placement-new noch von ::std::move tun irgendwas. Wo kommt dieser völlig überflüssige Check her und warum wird er nicht wegoptimiert? Das ist eine innere Schleife, raubt mir ein Register und macht mir die Funktion in der Summe zu lang, um geinlined zu werden.

Edit: Selbst, wenn ich per __assume(nullptr < &Array[Index]); direkt vor dem new den trust-me-on-this-one-Modus erzwinge – er testet weiter. Wtf?!?

Re: Is it just me, or …

Verfasst: 13.06.2010, 16:46
von Alexander Kornrumpf
In dem Augenblick, in dem ich 128 hinschriebe, würden beide Operanden als signed short behandelt, weil 128 zu groß für char ist.
Was mich ehrlich gesagt in der These bestätigt, dass das ein bekloppter(das wäre zu zeigen) Randfall(das ist nun per Definition unstrittig) ist.

Mal ehrlich, wieviele real-life Situationen fallen dir ein, in denen der Programmierer zwar denkt: "Mensch, bloß keine 128 schreiben, das passt nicht in char", aber dann nicht auch denkt "Hmm, Moment mal, der Vergleich an sich ist ja sinnlos", und das selbst wegoptimiert.

Re: Is it just me, or …

Verfasst: 13.06.2010, 16:51
von Krishty
Meine derzeitige Real-Life-Situation ist ein Parser:

Code: Alles auswählen

bool IsNonControlASCII(char x) {
    return (0x20 <= x) && (0x7F >= x);
}
Du darfst den zweiten Vergleich dort nicht weglassen, weil die Existenz eines Vorzeichens in char implementation-defined ist. Der Compiler hingegen darf es schon, weil er im Gegensatz zu dir erkennen kann ob das Programmverhalten dadurch beeinträchtigt würde oder nicht.

Eine Mögliche Alternative wäre natürlich das explizite return (0x20 <= static_cast<signed char>(x));, da hast du Recht. Aber der Compiler soll ja gerade das optimieren, was ich nicht selber optimiere weil ich zu faul, schöngeistig oder dumm bin, darin liegt doch der Sinn eines Optimizers … und nachdem ich gestern gesehen habe, wie er eine Verzweigung aus Zuweisungen und bitweisen Manipulationen zu einem komplett sprungfreien Bithaufen optimiert hat, hätte ich sowas Einfaches eigentlich schon erwartet.

Re: Is it just me, or …

Verfasst: 13.06.2010, 17:18
von Alexander Kornrumpf
Point taken.

Allerdings hoffe ich auf den Tag wo ASCII an sich ein "bekloppter Randfall" wird. Es ist eine Zumutung wie oft man auch heute noch Encodingproblemen begegnet.

Re: Is it just me, or …

Verfasst: 13.06.2010, 21:24
von TGGC
mea culpa. Bin wohl auf das gleiche wie Schromp reingefallen. f'`8k


Gruß, TGGC (der kostenlose DMC Download)

Re: Is it just me, or …

Verfasst: 13.06.2010, 21:57
von Krishty
Alexander Kornrumpf hat geschrieben:Allerdings hoffe ich auf den Tag wo ASCII an sich ein "bekloppter Randfall" wird. Es ist eine Zumutung wie oft man auch heute noch Encodingproblemen begegnet.
Bevor sie den Zeichensatz reformieren, sollten sie sich endlich mal auf vernünftige Line-Endings einigen ;)

Zurück zu dem Movement-Bug: Der Fehler scheint immer mit Placement-new aufzutreten, Move-Semantics haben nichts damit zu tun. Interessanterweise testet auch GCC 4.3.3 – allerdings nicht (wie VC 2010) die Zieladresse, sondern die Quelladresse. Aber genau wie VC springt er im Fall eines Nullzeigers einfach über die Konstruktion hinweg – kein Error-Handling, nichts. Da es beide Compiler tun, glaube ich nicht unbedingt an einen Fehler.

Hat jemand eine Idee, warum nach Placement-new und vor der Konstruktion absichtlich auf nullptr getestet werden sollte?

Hier mal ein Test:

Code: Alles auswählen

struct NonTrivialType {
	int x;
	NonTrivialType() : x(0) { }
	NonTrivialType(NonTrivialType const & Ref) : x(Ref.x) { }
};

	…
	NonTrivialType * Array = new NonTrivialType[2];
	new (&Array[0]) NonTrivialType(Array[1]);
Die letzte Zeile kompiliert zu

Code: Alles auswählen

13F6E12DF  test        r11,r11
13F6E12E2  je          13F6E12EBh  
13F6E12E4  mov         eax,dword ptr [r11+4]
13F6E12E8  mov         dword ptr [r11],eax
13F6E12EB  …
statt zu

Code: Alles auswählen

13F6E12E4  mov         eax,dword ptr [r11+4]
13F6E12E8  mov         dword ptr [r11],eax

Re: Is it just me, or …

Verfasst: 13.06.2010, 23:00
von Jörg
Ich muss mich korrigieren, auch gcc testet das Ziel. Habe mit den Array-Indizees gespielt und bin durcheinander gekommen. placement-new muss wohl garantieren, auch im 0-Fall keine Exception zu werfen....ist natuerlich aergerlich, wenn man diesen per-se ausschliessen kann.

Re: Is it just me, or …

Verfasst: 14.06.2010, 18:04
von Krishty
Okay, Update:

Der C++-Standard sagt: Ist new mit throw() deklariert (wirft also keine Exceptions) und gibt nullptr zurück, darf keine Initialisierung stattfinden. Das gilt auch für das throw()-deklarierte Placement-new – dort liegt wahrscheinlich der Ursprung des Tests. Soweit, so gut.

Problematisch ist allerdings, dass der Compiler den Test nicht wegoptimiert, wenn die Adresse des Placements definitiv nicht nullptr ist … und das im Fall von VC sogar, wenn man es per __assume() zu erzwingen versucht. Das ist ein Bug im Optimizer.

Für GCC gibt es bereits einen entsprechenden Bug-Report, der 2005 gefiled wurde und immernoch offen ist. Für VC habe ich ihn gerade abgeschickt.

Re: Is it just me, or …

Verfasst: 11.07.2010, 14:36
von Krishty
VCs Optimizer arbeitet auch ganz toll, wenn man eine float als int reinterpretieren möchte. Falls ihr mal so eine Funktion schreibt: Übergebt nicht float, sondern float const & – sonst wird die Gleitkommazahl aus einem SSE-Register auf den Stack geschoben, von dort zurück ins SSE-Register geladen, wieder zurück auf den Stack geschrieben, dann in ein Integer-Register geladen und erst dann weiterverarbeitet. Am besten pfeift ihr auch auf Aliasing und macht dann einfach return reinterpret_cast<int const &> – damit scheint der Compiler am besten zurecht zu kommen.

Übrigens sind Integer-Vergleiche locker 10 % schneller als Gleitkommavergleiche – wenn ihr also massiv auf Basis von Gleitkommazahlen brancht, konvertiert ihr Bitmuster zu Integers im Zweierkomplement und vergleicht die.

Re: Is it just me, or …

Verfasst: 12.07.2010, 16:03
von Krishty
Jeden Tag was Neues, Adam …

Hier gejammert und wenig später reproduziert: Der Linker entfernt unreferenzierte Compile-Time-constant-Arrays mal so, mal so aus der Exe. In meinem Programm hat er sie nur wegoptimiert, wenn sie in Zeigern lagen; beim Reproduzieren für den Bug-Report hat er sie nur entfernt, wenn sie in Arrays lagen.

Man kann sich also scheinbar nicht darauf verlassen, dass alles unreferenzierte Zeug rausfliegt – und es auch nicht wirklich erzwingen. Sollte das mal zum Problem werden (bei wirklich großen Datenmengen), sollte man sich vielleicht überlegen, ob man nicht per Präprozessor steuert, von welchen Symbolen der Compiler überhaupt weiß.

Re: Is it just me, or …

Verfasst: 12.07.2010, 23:59
von kimmi
Oder man checkt seinen Code mit so etwas wie Lint bzw. einem g++, der einem Warnungen für nicht referenzierte Symbole ausspuckt.

Gruß Kimmi