Unterschiedliche Rechenergebnisse: Debug und Release



  • 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



  • life schrieb:

    Eine Compileroptimierung darf niemals das beobachtbare Verhalten eines (korrekten) Programms ändern.

    Es gibt zwei Situationen, in denen es der C++-Standard sogar ausdrücklich erlaubt: Bei Rückgabewerten von Funktionen und bei temporären Objekten darf eine unnötige Kopie vom Compiler wegoptimiert werden.

    Tomahawk schrieb:

    UB ?

    Undefined behaviour (undefiniertes Verhalten)



  • life schrieb:

    Eine Compileroptimierung darf niemals das beobachtbare Verhalten eines (korrekten) Programms ändern.

    Fast alle IDE's compiler verzichten in den Release Versionen auf auf frame pointer, agressives inlining und -O3 egal ob unter Windows oder Linux. Warum wohl?

    Wenn es nichts am Programmfluss ändern würde, dann bräuchte man diese Optionen in Release nicht weil sie immer default on wären.



  • Nexus schrieb:

    life schrieb:

    Eine Compileroptimierung darf niemals das beobachtbare Verhalten eines (korrekten) Programms ändern.

    Es gibt zwei Situationen, in denen es der C++-Standard sogar ausdrücklich erlaubt: Bei Rückgabewerten von Funktionen und bei temporären Objekten darf eine unnötige Kopie vom Compiler wegoptimiert werden.

    Ja, das stimmt. Aber das sind eben Ausnahmen, die im Standard genau definiert sind. Wenn ein optimierender Compiler machen dürfte, wozu er gerade Lust hat, würden diese Ausnahmen doch wohl kaum im Standard vermerkt sein, oder? 😉

    @Tomahawk: Das UB kann ja im Prinzip *irgendwo* auftreten und dadurch in einem ganz anderen Teil des Programms deine Addition zerschießen. Das es daran liegt, ist aber von mir nur geraten. Es könnte natürlich auch tatsächlich ein Compilerbug sein..



  • Ich experimentiere mal weiter, glaube aber kaum, dass ich das Problem verstehen werde und "meinen Frieden finde".

    Der Menüpunkt "Inlinefunktionserweiterung" Ob1 oder Ob2 produzieren bei mir das Problem. Mit Ob0 oder Standard arbeitet die Addition korrekt.

    Kann man dieses Verhalten interpretieren?

    Es ist auch daher interessant, dass die Berechnung korrekt ausgeführt wird, sobald man irgendeine "unsinnige" Codezeile reinschreibt, zB ein print(). Dann klappt es wieder?

    Gruss



  • Im MSDN Library habe ich folgende Information zu Option Ob gefunden:

    Hinweis
    Informationen, die bei Testläufen für die Profilerstellung erfasst wurden, überschreiben Optimierungen, die sonst bei Angabe von /Ob, /Os oder /Ot aktiv sind. Weitere Informationen finden Sie unter Profilgesteuerte Optimierungen (PGO).

    Da ich im Release meist mit PGO und Testszenarien compiliere, kann ich mir diese Compileroptionen zur Optimierung womöglich sparen?! Übrigens führt das PGO Kompilat - im Gegensatz zum normalen Release - eine korrekte Berechnung durch.

    Jetzt such ich nur noch nach dem Beweis, dass mein Code diesen Fehler nicht verursacht.

    Gruss



  • Ich würde ja an deiner Stelle mal versuchen den Fehler weiter einzugrenzen. Z.B. behauptest du, dass der Compiler bei

    50000 + piece_value_midgame(move_piece_captured(move)) - move_piece(move);
    

    die Addition nicht berücksichtigt. Dann lass doch mal piece_value_midgame einfach 7 und move_piece 5 zurückgeben. Kommt dann trotzdem 50000 raus? Usw.

    Edit: So grad mal kompiliert. Assember-output:

    ; 24   :   {
    ; 25   :     temp = 50000 + piece_value_midgame(move_piece_captured(move)) - move_piece(move);
    
    	test	ebx, 32768				; 00008000H
    $LN99@sort_c:
    	cmovne	edx, esi
    	mov	eax, ebx
    	shr	eax, 6
    	and	eax, 63					; 0000003fH
    	movsxd	rcx, eax
    	movsxd	rax, edx
    	mov	r13d, DWORD PTR ?piece_value_mg@?1??piece_value_midgame@@YAHH@Z@4QBHB[r14+rax*4]
    	sub	r13d, DWORD PTR ?B@@3Uboard_c@@A[r14+rcx*4+256]
    	add	r13d, 50000				; 0000c350H
    
    ; 26   :     current->value = temp;
    
    	mov	DWORD PTR [rdi+76], r13d
    

    Meinen bescheidenen Assemblerkenntnissen zufolge, führt add eine Addition durch. D.h. die Addition wurde wohl doch nicht wegoptimiert ;).


Anmelden zum Antworten