Unterschiedliche Rechenergebnisse: Debug und Release



  • y not? schrieb:

    Aber wie macht man denn sonst ne fuzzy Stellungsbewertung?
    Oder macht man sie nicht fuzzy?

    Festkommazahlen würde ich vielleicht nehmen. Also blanke integers, die nur zur Bildschirmausgabe durch was festes (hier 2^23) geteilt werden.
    Nehmen wir mal einen reinen Materialzähler nach http://de.wikipedia.org/wiki/Schachfigur#Tauschwert
    und 3+3+3+3+5+5+9+ 8*9 (8 zur Dame umgewandelte Bauern)=103, für den König nehme ich dann mal 127 an, weil den zu verlieren ist schlimmer als alles andere, dann brauche ich 8 Bit und eins für das Vorzeichen und kann bei einem int32 noch 23 Nachkommabits nehmen.



  • Habe die Stelle im Code gefunden, wo es einen Unterschied zwischen den Kompilaten gibt.

    x32 Release
    x32 Debug
    x64 Debug
    x64 Release PGO

    Alle Kompilate führen zu identischen Ergebnissen. Nur das x64 Release hat Abweichungen. In folgendem Code wird eine Addition nicht ausgeführt:

    void sort_c::score_evasions(bool print) {
      if (last_move - move_list <= 1) return;
    
      for (move_info_c * current = move_list; current != last_move; current++) {
        int move = current->move;
        int value = static_exchange_evaluation(move);
    
        if (value < 0) {
          current->value = value;
        }
        else if (move_is_capture(move)) {
          current->value =   piece_value_midgame(move_piece_captured(move))  // Addition wird nicht ausgeführt
                           - move_piece(move)
                           + HISTORY_MAX;  // 50000
        }
        else {
          current->value = history_move_ordering_score(move);
        }
    
        if (print) {
          if (move_from(move) == SQUARE_E4) {
            char string[256];
            printf_s("value = %d\n", current->value);
            fflush(stdout);
          }
        }
      }
    }
    

    current->value muss sich wie folgt berechnen:
    100 - 0 + 50000 = 50100

    Der Compiler erzeugt aber nur 50000. Habe alle Daten genau überprüft und kann nichts finden. Meine einzige Erklärung, dass der 64-Bit Compiler von MS Visual Studio 2010 Professional fehlerhaft compiliert?! Verstehe aber nichts von Assembler, daher bringt es mich nicht weiter mit die Assemblerausgaben anzusehen.

    Probiere jetzt den 2008er Compiler aus.

    Ich verstehe es nicht.

    Gruss
    T.



  • Sobald ich die Compilereinstellungen für Release x64 ändere addiert der Compiler wieder korrekt?!



  • Tomahawk schrieb:

    Sobald ich die Compilereinstellungen für Release x64 ändere addiert der Compiler wieder korrekt?!

    Der Code den du gepostet hast ist so nicht ausführbar. Wieso debuggst du ihn nicht einfach? Du könntest auch mal die Startwerte für einzelnen Variablen durchgeben.

    1. Geh Iteration für Iteration durch und schau ab wann der Fehler auftaucht. Wenn es viele Ergebnisse sind dann gebe zwei Dateien aus und dann siehst du ab welcher Iteration der Wert unterschiedlich ist.

    2. Poste mal die Compiler Flags.

    3. Wenn du irgendwelche Optimierungen im Release Modus nutzt, dann schalte sie Schritt für Schritt aus.

    4. Ich habe auch die VS2010 und diese hat einen sehr angenehmen Debug Modus.



  • darkfate schrieb:

    Tomahawk schrieb:

    Sobald ich die Compilereinstellungen für Release x64 ändere addiert der Compiler wieder korrekt?!

    Der Code den du gepostet hast ist so nicht ausführbar. Wieso debuggst du ihn nicht einfach? Du könntest auch mal die Startwerte für einzelnen Variablen durchgeben.

    1. Geh Iteration für Iteration durch und schau ab wann der Fehler auftaucht. Wenn es viele Ergebnisse sind dann gebe zwei Dateien aus und dann siehst du ab welcher Iteration der Wert unterschiedlich ist.

    2. Poste mal die Compiler Flags.

    3. Wenn du irgendwelche Optimierungen im Release Modus nutzt, dann schalte sie Schritt für Schritt aus.

    4. Ich habe auch die VS2010 und diese hat einen sehr angenehmen Debug Modus.

    Leider bin ich im Lesen von Assemblercode nicht so fit. Daher kann ich die "fehlerhafte" Release Engine nicht debuggen. Die Debugversion habe ich schon gründlich untersucht.

    Hier die Compilerflags:

    /nologo /W4 /WX- /O2 /Ob2 /Oi /Ot /Oy /GT /GL /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /GF- /Gm- /MT /GS- /Gy /arch:SSE2 /fp:fast /Zc:wchar_t /Zc:forScope /GR- /Fp"x64\Release\Loop 2010.pch" /Fa"x64\Release\" /Fo"x64\Release\" /Fd"x64\Release\vc100.pdb" /Gr /errorReport:queue /favor:INTEL64

    Sobald ich die Optimierungsflags zurücksetzte (zB O2) gibts keine Unregelmässigkeiten mehr.

    Gruss
    T.



  • Mit dem MS 2008 Compiler gibts keine Unregelmässigkeiten.

    Bisher ist es tatsächlich nur das 64-Bit Release Build des MS 2010 Compilers mit obigen Flags.

    Auch verschiedene Versionen des BoundsChecker konnten nach mehrtägigen Analysen keine Probleme feststellen.

    Gruss



  • Tomahawk schrieb:

    Leider bin ich im Lesen von Assemblercode nicht so fit.

    Wenn Assembler überhaupt nicht geht, dann hilft nur die gute alte Fileausgabe, oder cout, so dass du dir den Status aller Variablen in der Console oder irgend einer Textbox ausgeben lässt. Datei ist besser, weil du siehst welcher Thread wann zuerst schreibt oder priorisiert wird.

    Alternativ lässt sich der optimierte Code, der vor dem kompilieren erstellt wurde auch behalten ohne ihn nach dem optimieren zu löschen. Aber dieser ist nach getätigten Optimierungen manchmal fast genau so aufschlussreich wie Assemblercode

    Oder du gibst, falls es möglich ist, die Startwerte der Variablen.

    Hier die Compilerflags:

    Tomahawk schrieb:

    /nologo /W4 /WX- /O2 /Ob2 /Oi /Ot /Oy /GT /GL /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /GF- /Gm- /MT /GS- /Gy /arch:SSE2 /fp:fast /Zc:wchar_t /Zc:forScope /GR- /Fp"x64\Release\Loop 2010.pch" /Fa"x64\Release\" /Fo"x64\Release\" /Fd"x64\Release\vc100.pdb" /Gr /errorReport:queue /favor:INTEL64

    Hui, du hast alles angeschaltet was sich nach irgendwie Geschwindigkeit angehört hat. Du solltest hingehen und den Profiler nutzen. Manchmal haben die Einstellungen auch keinen Mehrwert und bringen nur aufgeblähten Code mit sich.

    Nebenbei: Man sollte nur den Rechenteil so intensiv optimieren. Besser: Mit schnelleren Algorithmen als mit agressiven Compiler Flags. GUI oder ähnliches nur mit Standard Release Einstellungen optimieren.

    Wahrscheinlich ist auch dass alle Release Versionen irgendwo ein Problem haben, nur zeigt es sich an einer Stelle, die du zum Glück beobachten kannst.

    Zuerst: Schalte mal nur /MP ab und versuche es noch einmal mit den selben Einstellungen. Wenn sich die beiden beißen, dann muss man die for Schleife ein wenig überdenken.

    Nächster Vorschlag. Du stellst überall die Release Optimierungs-Einstellungen auf Default.

    Wenn du SSE2 Nutzt, dann solltest du das Alignment auf 128Bit setzen:
    Lies dir bei Gelegenheit das mal durch: http://msdn.microsoft.com/en-us/library/aa290049(VS.71).aspx
    Compiler Flag /Zp16

    Flag /GT nur wenn du Fibers oder TLS nutzt.

    Tomahawk schrieb:

    Sobald ich die Optimierungsflags zurücksetzte (zB O2) gibts keine Unregelmässigkeiten mehr.

    Riecht nach einer Überoptimierung.



  • Bin ratlos und meine Kollegen bei den Softwarentwicklern haben auch keine Idee mehr. Vielleicht liegt es an den Kompilereinstellunge, aber darüber kann ich keine Aussage machen.

    Warum werde im x64 Release Modus 2 int Zahlen nicht addiert.

    Gibt es die Möglichkeit den Quellcode hier zu veröffentlichen (~1500 Zeilen) und mal Jemanden mit Ahnung von Assembler und Compiler Settings drüberschauen zu lassen?

    Vollkommen ratlos und erschöpft... 😞



  • Hier mal den Code:

    class sort_c {
    public:
      sort_c();
    
    private:
      move_info_c * last_move;
      move_info_c move_list[256];
    };
    
    sort_c::sort_c() {
      char string[256];
      int temp;
    
      last_move = generate_evasions(move_list);
    
      move_info_c * current = move_list+8;
    
      int move = current->move;
      int value = static_exchange_evaluation(move);
    
      temp = 0;
    
      if (value < 0) {
        current->value = value;
      }
      else if (move_is_capture(move)) {
        // hier wird nicht addiert
        temp = 50000 + piece_value_midgame(move_piece_captured(move)) - move_piece(move);
        current->value = temp;
      }
      else {
        current->value = 0;
      }
    
      printf_s("move............%s %d\n", move_to_string(move, string), move);
      printf_s("see value.......%d\n", value);
      printf_s("piece captured..%d\n", move_piece_captured(move));
      printf_s("piece value.....%d\n", piece_value_midgame(move_piece_captured(move)));
      printf_s("piece...........%d\n", move_piece(move));
      printf_s("value...........%d\n", current->value);
      printf_s("temp............%d\n", temp);
      printf_s("is enpassant....%d\n", move_is_enpassant(move));
      printf_s("\n");
      fflush(stdout);
    }
    

    Es werden in einer Stellung 9 Züge generiert. Danach untersuche ich den betreffenden Zug Nr 9, also move_list+8. Die print-Ausgaben stimmen zwar alle, aber temp und current->value ergeben nur 50000 statt 50198 (wie es sein sollte und von allen gtesteten Compilern gemacht wird).



  • Ich würde meine eMail Adresse hier angeben, wer möchte kann mich kurz anschreiben und ich sende den Code zu.

    Gruss
    T.



  • Die Ausgabe des Programmes:

    move............e4f3 34581
    see value.......0
    piece captured..0
    piece value.....198
    piece...........0
    value...........50000
    temp............50000
    is enpassant....1
    

    Hier erkennt man doch ganz klar, dass piece_value_midgame(move_piece_captured(move)) 198 ist und move_piece(move) 0. Also ist die Summe doch temp = 50000 + 198 - 0;

    Was sehe ich und meine Kollegen nicht?



  • Tomahawk schrieb:

    Sobald ich die Optimierungsflags zurücksetzte (zB O2) gibts keine Unregelmässigkeiten mehr.

    Hier ist auch die Lösung des Problems. Die Compilereinstellungen passen nicht zu deinem Code. Lade eine pw geschützte Zip Datei bei Rapidshare hoch oder sowas.



  • Super, danke!

    Hier der Download Link:

    http://rapidshare.com/files/403808358/Problem.rar.html

    Passwort ist unnötig, da nichts besonderes drin steht.

    Bitte unbedingt unter MS Visual Studio 2010 Professional x64 Release testen. Und mit den Compilereinstellungen (sind beim Download enthalten).

    Bin gespannt auf die Interpretation des Problems, kann es mir mit meinen Kenntnissen nicht erklären.

    Gruss
    T.





  • Man kann ja dem Compiler beim optimieren ein wenig Sand ins Getriebe streuen, dann gehts. Eine sinnlose bool Zuweisung kann da helfen. 🙂 Fürs erste natürlich. Meine Zeit ist im Moment begrenzt.

    #include <stdio.h>
    #include "sort.h"
    #include "value.h"
    #include "board.h"
    
    bool dummy = false;
    
    sort_c::sort_c() {
    	char string[256];
    	int temp;
    
    	last_move = generate_evasions(move_list);
    
    	move_info_c * current = move_list+8;
    
    	int move = current->move;
    	int value = static_exchange_evaluation(move);
    
    	temp = false;
    
    	if (value < 0) current->value = value;
    	else if (move_is_capture(move)){
    		dummy=0;
    		current->value = 50000 + piece_value_midgame(move_piece_captured(move)) - move_piece(move);
    	}
    	else current->value = 0;
    
    	printf_s("move............%s %d\n", move_to_string(move, string), move);
    	printf_s("see value.......%d\n", value);
    	printf_s("piece captured..%d\n", move_piece_captured(move));
    	printf_s("piece value.....%d\n", piece_value_midgame(move_piece_captured(move)));
    	printf_s("piece...........%d\n", move_piece(move));
    	printf_s("value...........%d\n", current->value);
    	printf_s("temp............%d\n", temp);
    	printf_s("is enpassant....%d\n", move_is_enpassant(move));
    	printf_s("\n");
    	fflush(stdout);
    }
    
    // X64 Release
    move............e4f3 34581
    see value.......0
    piece captured..0
    piece value.....198
    piece...........0
    value...........50198
    temp............0
    is enpassant....1
    


  • darkfate schrieb:

    Tomahawk schrieb:

    Sobald ich die Optimierungsflags zurücksetzte (zB O2) gibts keine Unregelmässigkeiten mehr.

    Hier ist auch die Lösung des Problems. Die Compilereinstellungen passen nicht zu deinem Code.

    Was soll das denn bedeuten? Eine Compileroptimierung darf niemals das beobachtbare Verhalten eines (korrekten) Programms ändern.

    Ich vermute mal, dass irgendwo im Programm UB auftritt und dieses erst durch die Optimierungen sichtbar wird.



  • life schrieb:

    Was soll das denn bedeuten? Eine Compileroptimierung darf niemals das beobachtbare Verhalten eines (korrekten) Programms ändern.

    Stimmt nicht. Wenn ich meinen Linux Kernel kompiliere, dann sind manche Optimierungseinstellungen in der Optimierungsstufe "-O3" sogar tabu.



  • Der Linux Kernel ist auch nicht standard-konform.



  • life schrieb:

    Der Linux Kernel ist auch nicht standard-konform.

    Man kann nicht alle Probleme Standardkonform lösen.



  • life schrieb:

    darkfate schrieb:

    Tomahawk schrieb:

    Sobald ich die Optimierungsflags zurücksetzte (zB O2) gibts keine Unregelmässigkeiten mehr.

    Hier ist auch die Lösung des Problems. Die Compilereinstellungen passen nicht zu deinem Code.

    Was soll das denn bedeuten? Eine Compileroptimierung darf niemals das beobachtbare Verhalten eines (korrekten) Programms ändern.

    Ich vermute mal, dass irgendwo im Programm UB auftritt und dieses erst durch die Optimierungen sichtbar wird.

    UB ?

    Diese Programmteile sind genau durchgecheckt. Ausschliessen möchte ich einen Fehler natürlich nicht. Aber in den wenigen Code-Zeilen kann ich nichts entdecken.

    Gruss


Anmelden zum Antworten