11.4.4.4 : Débogage
La validation d'un calcul parallèle est extrêmement ardue. En effet, il est très courant que le résultat d'un calcul parallèle dépende du détail de l'exécution de chaque thread, qui peut lui-même changer d'une exécution à l'autre au gré des fantaisies des mécanismes de synchronisation utilisé.
Très souvent, ces changements de résultat sont parfaitement anodins, et uniquement le fruit d'une absence d'associativité des calculs flottants ou d'une génération de nombres aléatoires différente. Voir à ce sujet le chapitre 11.8.
Mais malheureusement, il est difficile en pratique de discerner une fluctuation liée aux réordonnancements d'une erreur de calcul liée à un ordre d'exécution des threads imprévu. Une reproductibilité parfaite des résultats par rapport au calcul séquentiel est donc parfois exigée, mais cette contrainte de conception peut impliquer un temps de développement beaucoup plus important, une utilisation des ressources matérielles à l'exécution plus forte, et produire à terme une code qui passe moins bien à l'échelle. Tout cela étant dû au fait que le code parallèle doit potentiellement se synchroniser beaucoup plus pour reproduire exactement certains aspects du comportement séquentiel.
De plus, la reproduction exacte des résultats du calcul séquentiel n'est en aucun cas un gage de plus grande précision, car les réductions de données parallèles qui causent ces fluctuations des résultats produisent souvent en réalité des résultats plus précis qu'une simple accumulation séquentielle noteCet effet est dû à la nature relative de la précision en virgule flottante. Lorsqu'on effectue une accumulation séquentielle naïve d'un ensemble de nombres en virgule flottante, on finit souvent à terme par ajouter des petites quantités à un accumulateur dont la valeur est grande, ce qui fait que certaines décimales des données d'entrées sont ignorées. La réduction parallèle des données, qui utilise un plus grand nombre d'accumulateurs, est moins sujette à ce problème.. Pour un autre exemple d'augmentation de précision liée à l'optimisation, mais cette fois dans un contexte de vectorisation, consulter le chapitre 11.3.
Quand un problème est trouvé, la reproduction et l'analyse de celui-ci peut être difficile~:
- Le problème peut se présenter sur une machine mais pas sur une autre, du fait de différences de niveau de parallélisme ou même de micro-architecture CPU.
- Il peut ne se produire que très rarement, et nécessiter de passer par des centaines d'exécutions justes avant d'observer un résultat faux. Quand l'application est interactive (ex~: visualisation), on peut alors devoir ajouter un mode d'exécution non interactif.
- L'instrumentation manuelle du programme peut faire disparaître le problème en changeant des décisions du compilateur ou en ajoutant des petits délais d'exécution qui rendent la situation problématique beaucoup moins probable.
- Les outils d'investigation automatisés, comme les débogueurs, rencontrent une partie de ces problèmes et ont aussi une ergonomie bien moins bonne quand ils sont appliqués à des programmes parallélisés (lorsque la possibilité existe).
Des outils de débogage spécialement adaptés aux programmes distribués sont parfois développés par les équipes des super-calculateurs. Si l'intention est louable, ils sont souvent bien moins flexibles que leurs homologues pour codes séquentiels, difficiles à installer sur d'autres machines que le super calculateur pour lequel ils ont été conçus, et ils ne supportent généralement que la distribution de calculs via MPI.