Unterschiedliche Rechenergebnisse: Debug und Release
-
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....1Hier 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_midgameeinfach 7 undmove_piece5 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], r13dMeinen bescheidenen Assemblerkenntnissen zufolge, führt
addeine Addition durch. D.h. die Addition wurde wohl doch nicht wegoptimiert ;).
-
Alles arbeitet nach unserem Ermessen fehlerfrei. Aber der Microsoft Visual Studio 2010 Professional Compiler mit der Einstellung x64 Release Ob1 oder Ob 2 kompiliert fehlerhaft. Die Ausgabe in der Console muss am Ende heissen:
value........50100 temp.........50100Hier der gesamte und stark vereinfachte Code (kürzer geht's kaum). Am besten rauskopieren und unter Visual Studio 2010 selbst staunen.
#include <stdio.h> typedef unsigned __int64 bitboard_t; // long long const int IS_ENPASSANT = 1 << 15; struct move_info_c { int move; int value; }; struct board_c { int color_on[64]; int piece_on[64]; bitboard_t piece_bb[6]; bitboard_t color_bb[2]; }; class sort_c { public: sort_c(); move_info_c * last_move; move_info_c move_list[256]; }; static bitboard_t pawn_attack_bb[2][64]; static board_c B; static void initialize_fen_position(const char * position, const char * to_move, const char * castle, const char * enpassant); static move_info_c * generate_evasions(move_info_c * move_list); static int static_exchange_evaluation(int move); static void initialize_bitboards(); inline int move_to(int move) { return move & 63; } inline int move_from(int move) { return (move >> 6) & 63; } inline int move_is_enpassant(int move) { return (move & IS_ENPASSANT) != 0; } inline int make_enpassant(int from, int to) { return to | (from << 6) | IS_ENPASSANT; } inline int square_to_file(int square) { return square & 7; } inline int square_to_rank(int square) { return square >> 3; } inline int file_and_rank_to_square(int file, int rank) { return file + (rank << 3); } inline int piece_value_midgame(int piece) { int piece_value_mg[7] = {100, 300, 400, 500, 900, 1000, 0}; return piece_value_mg[piece]; } inline bitboard_t file_and_rank_to_bitboard(int file, int rank) { return static_cast<bitboard_t>(1) << static_cast<bitboard_t>(file_and_rank_to_square(file, rank)); } inline bitboard_t pawn_attack_bitboard(int color, int square) { return pawn_attack_bb[color][square]; } inline int color_on_square(int square) { return B.color_on[square]; } inline int piece_on_square(int square) { return B.piece_on[square]; } inline bitboard_t pawn_bitboard(int color) { return B.piece_bb[0] & B.color_bb[color]; } inline int move_is_capture(int move) { return (piece_on_square(move_to(move)) != 6) || move_is_enpassant(move); } inline int move_piece(int move) { return piece_on_square(move_from(move)); } inline int move_piece_captured(int move) { return move_is_enpassant(move) ? 0 : piece_on_square(move_to(move)); } void initialize_bitboards() { for (int square = 0; square < 64; square++) { int file = square_to_file(square); int rank = square_to_rank(square); pawn_attack_bb[0][square] = 0; pawn_attack_bb[1][square] = 0; if (file - 1 >= 0 && rank + 1 <= 7) pawn_attack_bb[0][square] |= file_and_rank_to_bitboard(file - 1, rank + 1); if (file + 1 <= 7 && rank + 1 <= 7) pawn_attack_bb[0][square] |= file_and_rank_to_bitboard(file + 1, rank + 1); if (file - 1 >= 0 && rank - 1 >= 0) pawn_attack_bb[1][square] |= file_and_rank_to_bitboard(file - 1, rank - 1); if (file + 1 <= 7 && rank - 1 >= 0) pawn_attack_bb[1][square] |= file_and_rank_to_bitboard(file + 1, rank - 1); } } void initialize_fen_position() { for (int square = 0; square < 64; square++) { B.color_on[square] = 2; B.piece_on[square] = 6; } B.color_on[28] = 1; B.piece_on[28] = 0; B.color_on[29] = 0; B.piece_on[29] = 0; } move_info_c * generate_evasions(move_info_c * move_list) { (move_list++)->move = make_enpassant(28, 21); return move_list; } int static_exchange_evaluation(int move) { int from = move_from(move); int to = move_to(move); int me = color_on_square(from); int you = me ^ 1; int piece = piece_on_square(from); int piece_captured = move_piece_captured(move); if ( (pawn_attack_bitboard(me, to) & pawn_bitboard(you)) && (piece_value_midgame(piece) > piece_value_midgame(piece_captured)) && (piece_value_midgame(piece) > 100)) { return piece_value_midgame(piece_captured) - piece_value_midgame(piece); } return 0; } sort_c::sort_c() { int temp = 0; last_move = generate_evasions(move_list); move_info_c * current = move_list; int move = current->move; int value = static_exchange_evaluation(move); if (value < 0) { current->value = value; } else if (move_is_capture(move)) { temp = 50000 + piece_value_midgame(move_piece_captured(move)) - move_piece(move); current->value = temp; } else { current->value = 0; } printf_s("value...........%d\n", current->value); printf_s("temp............%d\n", temp); } int main() { initialize_bitboards(); initialize_fen_position(); sort_c s; return 0; }
