Unterschiedliche Ergebnisse zwischen Debug und Release
Habe ich das problem, dass mein code gibt unterschiedliche Ergebnisse beim Vergleich von debug zu release. Ich habe überprüft, dass beide Modi mit /fp:precise, also das sollte nicht das problem sein. Das Hauptproblem habe ich mit diesem ist, dass die komplette image-Analyse (es ist ein Bild Verständnis project) ist vollständig deterministisch, es gibt absolut nichts zufällig.
Ein weiteres Problem mit diesem ist die Tatsache, dass meine Version tatsächlich immer liefert das gleiche Ergebnis (23.014 für die Bild), wenn der debug-gibt einen zufälligen Wert zwischen 22 und 23, die sollten einfach nicht sein. Ich habe bereits geprüft, ob es möglicherweise Faden verwandt, aber nur ein Teil des Algorithmus, die multi-Thread gibt der genau das gleiche Ergebnis für beide debug und release.
Was sonst noch hier passiert?
Update1: Der code, den ich jetzt verantwortlich für dieses Verhalten:
float PatternMatcher::GetSADFloatRel(float* sample, float* compared, int sampleX, int compX, int offX)
{
if (sampleX != compX)
{
return 50000.0f;
}
float result = 0;
float* pTemp1 = sample;
float* pTemp2 = compared + offX;
float w1 = 0.0f;
float w2 = 0.0f;
float w3 = 0.0f;
for(int j = 0; j < sampleX; j ++)
{
w1 += pTemp1[j] * pTemp1[j];
w2 += pTemp1[j] * pTemp2[j];
w3 += pTemp2[j] * pTemp2[j];
}
float a = w2 / w3;
result = w3 * a * a - 2 * w2 * a + w1;
return result / sampleX;
}
Update2:
Dies ist nicht reproduzierbar mit 32bit-code. Während der debug-und release-code wird immer der gleiche Wert für 32bit, es ist noch immer anders aus, die 64-bit version und die 64-bit-debug -, noch liefert einige absolut zufällige Werte.
Update3:
Okay, ich fand es, um sicher zu sein, verursacht durch OpenMP. Wenn ich es deaktivieren, funktioniert es einwandfrei. (sowohl Debug-als auch Release mit ein und demselben code, und beide haben OpenMP aktiviert).
Folgende code ist der code, gebe mir Mühe:
#pragma omp parallel for shared(last, bestHit, cVal, rad, veneOffset)
for(int r = 0; r < 53; ++r)
{
for(int k = 0; k < 3; ++k)
{
for(int c = 0; c < 30; ++c)
{
for(int o = -1; o <= 1; ++o)
{
/*
r: 2.0f - 15.0f, in 53 steps, representing the radius of blood vessel
c: 0-29, in steps of 1, representing the absorption value (collagene)
iO: 0-2, depending on current radius. Signifies a subpixel offset (-1/3, 0, 1/3)
o: since we are not sure we hit the middle, move -1 to 1 pixels along the samples
*/
int offset = r * 3 * 61 * 30 + k * 30 * 61 + c * 61 + o + (61 - (4*w+1))/2;
if(offset < 0 || offset == fSamples.size())
{
continue;
}
last = GetSADFloatRel(adapted, &fSamples.at(offset), 4*w+1, 4*w+1, 0);
if(bestHit > last)
{
bestHit = last;
rad = (r+8)*0.25f;
cVal = c * 2;
veneOffset =(-0.5f + (1.0f / 3.0f) * k + (1.0f / 3.0f) / 2.0f);
if(fabs(veneOffset) < 0.001)
veneOffset = 0.0f;
}
last = GetSADFloatRel(input, &fSamples.at(offset), w * 4 + 1, w * 4 + 1, 0);
if(bestHit > last)
{
bestHit = last;
rad = (r+8)*0.25f;
cVal = c * 2;
veneOffset = (-0.5f + (1.0f / 3.0f) * k + (1.0f / 3.0f) / 2.0f);
if(fabs(veneOffset) < 0.001)
veneOffset = 0.0f;
}
}
}
}
}
Hinweis: mit Release-Modus und OpenMP aktiviert ist, bekomme ich das gleiche Ergebnis wie mit der Deaktivierung von OpenMP. Debug-Modus und OpenMP aktiviert, bekommt ein anderes Ergebnis, OpenMP deaktiviert, erhält das gleiche Ergebnis wie bei Release.
- Wir könnten mehr helfen, wenn wir sehen, einige code. Im Allgemeinen, meine Vermutung ist, dass Sie Locker syntax irgendwo, dass der normale compiler versteht richtig, aber der debugger nicht.
- verwenden valgrind zu überprüfen, wenn Sie einige Speicher-Korruption, die möglicherweise dazu führen, dass nicht deterministisch Verhalten.
- Interessant. Die üblichen Heisenbug situation ist, dass das debugging wird mehr zuverlässige Ergebnisse.
- Riecht wie ein Undefiniertes Verhalten...
- Release und debug sind nur verschiedene Sätze von Projekt-Optionen - Sie können ändern Sie die Optionen eins nach dem anderen, bis Sie finden, diejenigen, die in Ihrem Release-Ausgabe passen Sie Ihre Debug-Ausgabe. Aber wir haben nicht genug Informationen, um Ihnen zu sagen, was Los ist. Drucken Sie intermediate output, Teile und herrsche... 8 - )
- Hinzufügen intermediate output wird wahrscheinlich Ergebnisse als es die Kräfte einer Ordnung der Operationen auf den compiler an. Legen Sie eine printf-Anweisung und kann das problem gehen Weg, nehmen Sie es wieder aus und das problem gibt.
- Ich Neige dazu, zu widersprechen. Wenn er Einsätze printfs in der Mitte seiner Berechnung Schleifen dann ja etwas bekommen könnte nachbestellt werden. Aber wenn er ruft 10 numerische Routinen und überprüft die Eingabe - /Ausgabe-dann kann dieser Ansatz helfen, ihn zu finden Sie heraus, welche der Routinen geben unterschiedliche Ergebnisse bei debug und unter release. Wenn Sie stecken, können Sie versuchen, die Verengung, das problem...
- Hmmm...siehe Felix von Leitner ' s umfangreiche Präsentation auf die aktuelle assembly produziert, die von verschiedenen c-Compiler (PDF-link!). Moderne Compiler können-und wird schwer zu manipulieren-code.
- Sie haben viele unsynchronised Zugriffe auf shared-Variablen innerhalb der parallelen region
last
undbestHit
werden die meisten offensichtlichen. Diese fordert, dass Probleme auftreten, wenn der code ausgeführt wird.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Rücksicht auf mein Kommentar, das ist der code, dass ist wahrscheinlich die Wurzel des Problems:
last
ist nur zugewiesen werden, bevor es wieder Lesen, so ist es ein guter Kandidat für einelastprivate
variable, wenn Sie wirklich brauchen, wird der Wert aus der letzten iteration außerhalb der parallelen region. Ansonsten einfach machen esprivate
.Zugang zu
bestHit
,cVal
,rad
, undveneOffset
synchronisiert werden sollten, die von einer kritischen region:Beachten Sie, dass standardmäßig werden alle Variablen, mit Ausnahme der Zähler
parallel for
Schleifen und jene definiert, die innerhalb der parallelen region, die geteilt werden, d.h. dieshared
Klausel in Ihrem Fall tut nichts, es sei denn, Sie gelten auch für diedefault(none)
- Klausel.Andere Sache, die Sie sollten sich bewusst sein, dass in 32-bit-Modus von Visual Studio verwendet x87-FPU Mathematik, während im 64-bit Modus er verwendet SSE-math standardmäßig. x87-FPU hat Zwischenberechnungen mit 80-bit floating-point-Präzision (auch für Berechnungen mit
float
nur) während der SSE-Einheit unterstützt nur die standard-IEEE single und double-Genauigkeit. Einführung in OpenMP oder andere Parallelisierung Technik, um eine 32-bit-x87-FPU-code bedeutet, dass an bestimmten Punkten Zwischenwerte sollten zurück konvertiert werden, um die einzelnen Genauigkeit vonfloat
und wenn man ausreichend viele Zeiten zu einem leichten oder deutlichen Unterschied (abhängig von der numerischen Stabilität des Algorithmus) konnte beobachtet werden, dass zwischen den Ergebnissen aus den serial-code und die parallelen.Basierend auf deinem code, ich würde vorschlagen, die folgenden geänderten code geben Ihnen eine gute parallele Leistung, weil es keine synchronisation bei jeder iteration:
Es speichert nur die Werte der Parameter, die die besten Treffer in jeden thread und dann am Ende der parallelen region berechnet
rad
,cVal
undveneOffset
auf der Grundlage der besten Werte. Jetzt ist es nur eine kritische region, und es ist am Ende der code. Kann man dies umgehen, auch, aber Sie würden die Einführung eines zusätzlichen arrays.last
,r
,c
undk
geben, dass die besten Treffer in jeden thread in einem shared array (tun Sie es am Ende der parallelen region; das array sollte ein element pro thread; machenbestHist
privat), dann außerhalb der parallelen region untersuchen Sie das array und berechnenrad
,cVal
undveneOffset
auf der Grundlage der Werte aus dem thread, der die bestebestHit
Wert.Mindestens zwei Möglichkeiten:
Wenn Sie auf Windows, dann Valgrind nicht verfügbar ist (schade), aber man kann schauen hier für eine Liste von alternativen.
Eine Sache zu überprüfen ist, dass alle Variablen initialisiert sind. Viele Male un-optimierten code (Debug-Modus) initialisiert den Speicher.
Hätte ich gesagt: die Initialisierung der Variablen im debug-vs nicht im release. Doch Ihre Ergebnisse würden nicht wieder in diesem bis (zuverlässiges Ergebnis in der Ausgabe).
Macht Ihren code setzen auf spezifische offsets oder Größen? Debug-build würde Wachen bytes um einige Zuweisungen.
Könnte es sein, floating-point-Verbindung?
Den debug-floating-point-stack ist ein anderes release, das ist gebaut für mehr Effizienz.
Schau mal hier: http://thetweaker.wordpress.com/2009/08/28/debugrelease-numerical-differences/
Gerade über jede nicht definierte Verhalten kann für dieses Konto: initialisierten
Variablen, Schurke Zeiger, mehrere änderungen des gleichen Objekts
ohne eine dazwischenliegende Sequenz, die zeigen, etc. etc. Die Tatsache, dass die
die Ergebnisse sind manchmal unreproduceable argumentiert eher für eine
nicht initialisierte variable, es kann aber auch vorkommen von Zeiger-Probleme oder
bounds-Fehler.
Bewusst sein, dass die Optimierung ändern können Ergebnisse, vor allem auf einem Intel.
Optimierung ändern können, welche Zwischenwerte spill-Speicher, und
wenn Sie schon nicht gezielt verwendet Klammern, auch die Reihenfolge der Auswertung
in einem Ausdruck. (Und wie wir alle wissen, im Maschinen-floating-point -,
(a +
.) Noch sollten die Ergebnisse deterministisch sein:b) + c) != a + (b + c)
erhalten Sie unterschiedliche Ergebnisse, je nach dem Grad der Optimierung,
aber für eine beliebige Gruppe von Optimierungs-flags, sollten Sie die gleichen Ergebnisse erhalten.