Des résultats différents entre Debug et Release
J'ai le problème que mon code renvoie des résultats différents lors de la comparaison de débogage pour publication. J'ai vérifié que les deux modes d'utilisation /fp:précis, ce qui ne devrait pas être le problème. Le principal problème que j'ai avec c'est que l'image complète de l'analyse (c'est une image de la compréhension du projet) est complètement déterministe, il n'y a absolument rien au hasard en elle.
Un autre problème, c'est le fait que ma version de construire en réalité toujours renvoie le même résultat (23.014 pour l'image), tout en debug certains retours valeur aléatoire entre le 22 et le 23, le tout ne devrait pas être. J'ai déjà vérifié si elle peut être fil, mais la seule partie de l'algorithme qui est multi-thread renvoie précisément le même résultat pour le debug et release.
Quoi d'autre peut-être ce qui se passe ici?
Update1: Le code que j'ai maintenant trouvé responsables de ce comportement:
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:
Ce n'est pas reproductible avec 32 bits de code. Tout en debug et release code entraînera toujours la même valeur pour les 32 bits, c'est encore différent de la version 64bit version, et la version 64bit de débogage encore certains retours complètement aléatoire des valeurs.
Update3:
Bon, je l'ai trouvé certainement être causée par OpenMP. Quand je le désactiver, il fonctionne très bien. (Debug et Release utiliser le même code, et les deux ont OpenMP activé).
Voici le code à me donner de la difficulté à:
#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;
}
}
}
}
}
Remarque: avec la Version mode et OpenMP activé-je obtenir le même résultat qu'avec la désactivation d'OpenMP. Le mode de débogage et OpenMP activé obtient un résultat différent, OpenMP désactivé obtient le même résultat qu'avec la Libération.
- Nous pourrions être en mesure d'aider plus si nous voyons un peu de code. En général, je suppose que vous vous êtes en vrac à l'aide de la syntaxe quelque part que le compilateur comprend correctement, mais que le débogueur n'est pas.
- utiliser valgrind pour vérifier si vous avez certains de corruption de la mémoire qui peut entraîner la non déterministe du comportement.
- Intéressant. L'habitude Heisenbug situation est que le débogage obtient des résultats plus fiables.
- Sent comme un comportement indéterminé...
- Release et debug sont juste différents ensembles des options du projet - vous pouvez modifier les options une par une jusqu'à trouver ceux qui font de votre Libération de sortie correspondent à votre sortie de Débogage. Mais nous n'avons pas assez d'info pour vous dire ce qu'il se passe. Imprimer la production intermédiaire, diviser pour régner... 8 - )
- L'ajout de la production intermédiaire sera susceptible de changer les résultats, car il oblige à une commande d'opérations sur le compilateur. Insérer une instruction printf et le problème peut disparaître; le reprendre et le problème revient.
- J'ai tendance à être en désaccord. Si il insère des printfs dans le milieu de son calcul des boucles alors oui, quelque chose pourrait être réorganisées. Mais si il a des appels de 10 numérique des routines et des vérifications de l'entrée/sortie ensuite, cette approche peut aider à trouver les routines de donner des résultats différents en vertu de debug et en vertu de la libération. Si vous êtes coincé, vous pouvez précisez le problème...
- Hmmm...voir Felix von Leitner est présentation complet sur le montage lui-même produit par les différents compilateurs c (lien PDF!). Les compilateurs modernes peuvent et vont fortement manipuler votre code.
- Vous avez beaucoup de non synchronisés accès aux variables partagées à l'intérieur de la région parallèle,
last
etbestHit
être les plus évidentes. Cette appels pour des problèmes lors de l'exécution du code.
Vous devez vous connecter pour publier un commentaire.
Pour préciser mon commentaire, c'est le code qui est probablement la racine de votre problème:
last
est affecté uniquement à l'avant de la lire à nouveau c'est donc un bon candidat pour être unlastprivate
variable, si vous avez vraiment besoin de la valeur de la dernière itération à l'extérieur de la région parallèle. Sinon, il suffit de faireprivate
.Accès à
bestHit
,cVal
,rad
, etveneOffset
doivent être synchronisés par une région critique:Notez que par défaut, toutes les variables, sauf les compteurs de
parallel for
boucles et celles qui sont définies à l'intérieur de la région parallèle, sont partagés, c'est à dire lashared
clause dans votre cas ne fait rien à moins que vous pouvez aussi appliquer ledefault(none)
clause.Une autre chose que vous devez être conscient de est que en mode 32 bits de Visual Studio utilise FPU x87 mathématiques tout en mode 64 bits, il utilise de l'ESS en mathématiques par défaut. FPU x87 ne intermédiaire des calculs à l'aide de 80 bits à virgule flottante de précision (même pour les calculs impliquant des
float
seulement), tandis que l'ESS appareil prend uniquement en charge la norme IEEE simple et double précision. L'introduction OpenMP ou toute autre technique de parallélisation en 32 bits FPU x87 code signifie qu'à certains moments, les valeurs intermédiaires doivent être converties à la simple précision defloat
et si un nombre suffisant de fois une légère ou une différence significative (en fonction de la stabilité numérique de l'algorithme) a pu être observée entre les résultats à partir du numéro de série, et le parallèle.Basé sur votre code, je dirais que le suivant modifié code devrait vous donner de bonnes performances parallèles, car il n'y a pas de synchronisation à chaque itération:
Il stocke uniquement les valeurs des paramètres qui donnent les meilleurs résultats dans chaque thread et puis à la fin de la région parallèle, il calcule
rad
,cVal
etveneOffset
sur la base des meilleures valeurs. Maintenant il y a seulement une région critique, et c'est à la fin du code. Vous pouvez obtenir autour d'elle aussi, mais vous aurez à présenter des tableaux.last
,r
,c
etk
qui donnent les meilleurs résultats dans chaque thread dans un tableau partagé (à la fin de la région parallèle; le tableau doit avoir un élément par fil; fairebestHist
privé), puis à l'extérieur de la région parallèle examiner le tableau et calculerrad
,cVal
etveneOffset
sur la base des valeurs de la thread qui a le meilleurbestHit
valeur.Au moins deux possibilités:
Si vous êtes sur Windows, puis Valgrind n'est pas disponible (dommage), mais vous pouvez regarder ici pour une liste de solutions de rechange.
Une chose à vérifier, c'est que toutes les variables sont initialisées. À de nombreuses reprises de l'onu-code optimisé (mode Debug) pour initialiser la mémoire.
Je l'ai dit, l'initialisation d'une variable dans le debug vs pas on dans le communiqué. Mais vos résultats ne serait pas une copie de sauvegarde (résultat fiable dans libération).
Est-ce que votre code s'appuient sur aucun des décalages ou des tailles? Debug serait de placer des gardes octets autour de certaines allocations.
Pourrait-il être à virgule flottante sont-ils liés?
Le débogage à virgule flottante de la pile est différente de la version qui est construit pour plus d'efficacité.
Regardez ici: http://thetweaker.wordpress.com/2009/08/28/debugrelease-numerical-differences/
Juste au sujet de tout comportement indéfini peut tenir compte de cette: uninitialized
les variables, les voyous des pointeurs, plusieurs modifications d'un même objet
sans l'intermédiaire d'un point de séquence, etc. etc. Le fait que l'
les résultats sont à la fois unreproduceable plaide plutôt pour une
variable non initialisée, mais il peut également se produire à partir des problèmes avec le pointeur ou
limites des erreurs.
Être conscient que l'optimisation peut changer les résultats, en particulier sur un processeur Intel.
L'optimisation est possible de modifier les valeurs intermédiaires de déversement de la mémoire, et
si vous ne l'avez pas utilisée avec parcimonie parenthèses, même l'ordre d'évaluation
dans une expression. (Et comme nous le savons tous, dans la machine à virgule flottante,
(a +
.) Encore les résultats devraient être déterministe:b) + c) != a + (b + c)
vous obtiendrez des résultats différents selon le degré d'optimisation,
mais pour tout un ensemble d'options d'optimisation, vous devriez obtenir les mêmes résultats.