Es gibt viele technische Herausforderungen, die es äußerst schwierig machen, eine genaue Bit-für-Bit-Reproduzierbarkeit der Rechenergebnisse zu erreichen.
Auf Softwareebene können Änderungen am Code oder an einer der vom Code verwendeten Bibliotheken offensichtlich dazu führen, dass unterschiedliche Ergebnisse erzielt werden. Sie werden überrascht sein, wie viele Support-Bibliotheken zu einem typischen wissenschaftlichen Code verknüpft werden können.
Auf einer niedrigeren Ebene kann das Neukompilieren eines Codes oder einer der vom Code verwendeten Bibliotheken mit einem neuen Compiler oder mit aktivierten verschiedenen Compileroptimierungen ebenfalls Probleme verursachen. Ein Grund ist, dass verschiedene Operationen im Code möglicherweise in einer anderen Reihenfolge ausgeführt werden, wenn der Code neu kompiliert wird. Da die Gleitkommaaddition nicht assoziativ ist (a + b) + c <> a + (b + c), kann dies zu unterschiedlichen Ergebnissen führen.
OK, was ist, wenn wir die gesamte Softwareumgebung (Betriebssystem, Bibliotheken und kompilierten Code) erhalten, indem wir sie (z. B.) auf eine bootfähige CD-Rom brennen, auf der der Code ausgeführt wird? Können wir jetzt sicher sein, dass wir die gleichen Ergebnisse erzielen, wenn wir diesen Code auf einem anderen Computer ausführen?
Überraschenderweise variieren einige Codes tatsächlich die Reihenfolge der Berechnungen basierend auf Aspekten des jeweiligen Prozessormodells, auf dem sie ausgeführt werden. Beispielsweise lösen optimierte lineare Algebra-Bibliotheken normalerweise Matrixmultiplikationen auf, um an Blöcken zu arbeiten, die in den Cache passen. Wenn Intel einen neuen Mikroprozessor mit einem größeren Cache veröffentlicht, passt der Code möglicherweise die Blockgröße dynamisch an, was zu einer Arithmetik führt, die in einer anderen Reihenfolge ausgeführt wird und unterschiedliche Ergebnisse liefert. Andere Codes passen die Reihenfolge der Berechnungen dynamisch an die Größe des verfügbaren Speichers an. Wenn Sie den Code auf einem Computer mit mehr Speicher ausführen, kann dies dazu führen, dass die Arithmetik in einer anderen Reihenfolge ausgeführt wird und somit unterschiedliche Ergebnisse erzielt werden.
Die Dinge werden erstaunlich komplizierter, wenn Sie Multithread-Code einwerfen, da der genaue Ausführungsverlauf der verschiedenen Threads häufig nicht deterministisch ist und dies wiederum dazu führen kann, dass arithmetische Operationen von Lauf zu Lauf in einer anderen Reihenfolge ausgeführt werden.
In der Praxis können Sie höchstens auf Ergebnisse hoffen, die von einer Maschine zur nächsten ähnlich sind, bis hin zu den Genauigkeitstoleranzen der verwendeten Algorithmen. Wenn ich beispielsweise ein Problem mit der Wurzelfindung habe und die Halbierung verwende, um eine Wurzel auf + -1,0e-10 zu bringen, sollte ich glücklich sein, solange verschiedene Maschinen Antworten liefern, die innerhalb dieser Toleranz übereinstimmen.