Is it just me, or …

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Benutzeravatar
Krishty
Establishment
Beiträge: 8413
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

Habe die beiden nicht mehr installiert … probier es aus und poste hier :)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
jgl
Establishment
Beiträge: 109
Registriert: 08.04.2009, 08:58

Re: Is it just me, or …

Beitrag von jgl »

Würde ich gerne machen, aber wie weis ich nicht ;)
Benutzeravatar
Krishty
Establishment
Beiträge: 8413
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag 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.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
jgl
Establishment
Beiträge: 109
Registriert: 08.04.2009, 08:58

Re: Is it just me, or …

Beitrag 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...
Benutzeravatar
Krishty
Establishment
Beiträge: 8413
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Krishty »

Super. Damit wäre das reproduziert :) Falls du bei MSDN Connect bist, kannst du das Ticket ja bestätigen/uppen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
jgl
Establishment
Beiträge: 109
Registriert: 08.04.2009, 08:58

Re: Is it just me, or …

Beitrag 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 
    }
Benutzeravatar
Krishty
Establishment
Beiträge: 8413
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag 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.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Is it just me, or …

Beitrag 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 …
Benutzeravatar
Krishty
Establishment
Beiträge: 8413
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag 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.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8413
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag 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?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
TGGC
Establishment
Beiträge: 569
Registriert: 15.05.2009, 18:14
Benutzertext: Ich _bin_ es.
Alter Benutzername: TGGC
Echter Name: Ich _bin_ es.
Wohnort: Mainz
Kontaktdaten:

Re: Is it just me, or …

Beitrag 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)
Alexander Kornrumpf
Moderator
Beiträge: 2198
Registriert: 25.02.2009, 13:37

Re: Is it just me, or …

Beitrag 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.
Benutzeravatar
Schrompf
Moderator
Beiträge: 5397
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: Is it just me, or …

Beitrag 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.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Alexander Kornrumpf
Moderator
Beiträge: 2198
Registriert: 25.02.2009, 13:37

Re: Is it just me, or …

Beitrag 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.
Benutzeravatar
Schrompf
Moderator
Beiträge: 5397
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Schrompf »

Oh stimmt, hab mich von der verworrenen Schreibweise verwirren lassen.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Jörg
Establishment
Beiträge: 296
Registriert: 03.12.2005, 13:06
Wohnort: Trondheim
Kontaktdaten:

Re: Is it just me, or …

Beitrag von Jörg »

Vielleicht sollte man aufhoeren, MS VC als DEN Compiler zu sehen ;) Selbst der betagte gcc 4.3.3 bekommt es gebacken.
Benutzeravatar
Schrompf
Moderator
Beiträge: 5397
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: Is it just me, or …

Beitrag 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.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8413
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag 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.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Jörg
Establishment
Beiträge: 296
Registriert: 03.12.2005, 13:06
Wohnort: Trondheim
Kontaktdaten:

Re: Is it just me, or …

Beitrag 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.
Benutzeravatar
Krishty
Establishment
Beiträge: 8413
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag 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?!?
Zuletzt geändert von Krishty am 13.06.2010, 20:34, insgesamt 1-mal geändert.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Alexander Kornrumpf
Moderator
Beiträge: 2198
Registriert: 25.02.2009, 13:37

Re: Is it just me, or …

Beitrag 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.
Benutzeravatar
Krishty
Establishment
Beiträge: 8413
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag 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.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Alexander Kornrumpf
Moderator
Beiträge: 2198
Registriert: 25.02.2009, 13:37

Re: Is it just me, or …

Beitrag 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.
Benutzeravatar
TGGC
Establishment
Beiträge: 569
Registriert: 15.05.2009, 18:14
Benutzertext: Ich _bin_ es.
Alter Benutzername: TGGC
Echter Name: Ich _bin_ es.
Wohnort: Mainz
Kontaktdaten:

Re: Is it just me, or …

Beitrag von TGGC »

mea culpa. Bin wohl auf das gleiche wie Schromp reingefallen. f'`8k


Gruß, TGGC (der kostenlose DMC Download)
Benutzeravatar
Krishty
Establishment
Beiträge: 8413
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag 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
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Jörg
Establishment
Beiträge: 296
Registriert: 03.12.2005, 13:06
Wohnort: Trondheim
Kontaktdaten:

Re: Is it just me, or …

Beitrag 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.
Benutzeravatar
Krishty
Establishment
Beiträge: 8413
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag 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.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8413
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag 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.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8413
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Is it just me, or …

Beitrag 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ß.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
kimmi
Moderator
Beiträge: 1416
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: Is it just me, or …

Beitrag 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
Antworten