Comportement de flottement étrange dans OpenMP

J’utilise le code OpenMP suivant

#pragma omp parallel shared(S2,nthreads,chunk) private(a,b,tid) { tid = omp_get_thread_num(); if (tid == 0) { nthreads = omp_get_num_threads(); printf("\nNumber of threads = %d\n", nthreads); } #pragma omp for schedule(dynamic,chunk) reduction(+:S2) for(a=0;a<NREC;a++){ for(b=0;b<NLIG;b++){ S2=S2+cos(1+sin(atan(sin(sqrt(a*2+b*5)+cos(a)+sqrt(b))))); } } // end for a } /* end of parallel section */ 

Et pour NREC = NLIG = 1024 et valeurs supérieures, je reçois jusqu’à 7 accélérations sur une carte à 8 cœurs. Le problème est que si je compare les résultats finaux pour la variable S2, ils diffèrent entre 1 et 5% des résultats exacts obtenus dans la version en série. Quelle pourrait être la raison? Devrais-je utiliser certaines options de compilation spécifiques pour éviter ce comportement de flottement étrange?

L’ordre des additions / soustractions des nombres en virgule flottante peut affecter la précision.

Pour prendre un exemple simple, supposons que votre machine stocke 2 chiffres décimaux et que vous calculiez la valeur de 1 + 0.04 + 0.04.

  • Si vous faites l’addition de gauche en premier, vous obtenez 1,04, ce qui est arrondi à 1. La deuxième addition donnera 1 de nouveau, donc le résultat final est 1.

  • Si vous faites le premier ajout correct, vous obtenez 0,08. Ajouté à 1, cela donne 1,08 qui est arrondi à 1,1.

Pour une précision maximale, il est préférable d’append des valeurs de petit à grand.

Une autre cause pourrait être que les registres flottants sur la CPU peuvent contenir plus de bits que les flottants dans la mémoire principale. Par conséquent, si un résultat intermédiaire est mis en cache dans un registre, il est plus précis, mais s’il est transféré en mémoire, il est tronqué.

Voir aussi cette question dans la FAQ C ++ .

Il est connu que les opérations en virgule flottante de la machine sont erronées lorsque deux grandes valeurs sont soustraites (ou que deux grandes valeurs avec des signes différents sont ajoutées), ce qui donne la petite différence. Ainsi, la sommation d’une séquence de signe oscillé peut introduire une erreur grave à chaque itération. Un autre problème concerne le fait que la magnitude de deux opérandes est très différente, le moindre opérande s’annulant lui-même.
Il peut être utile de séparer les opérandes positifs et négatifs, d’effectuer la sommation de chaque groupe séparément, puis d’append (soustraire) les résultats du groupe.
Si la précision est cruciale, il faudra probablement effectuer un sorting préalable de chacun des groupes et effectuer deux sums à l’intérieur de chacun. La première sum ira du centre vers la plus grande (tête), la seconde ira du plus petit (queue) vers le centre. La sum du groupe résultant sera la sum des exécutions partielles.