PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Unterschiede FloatPoint-Berechnungen zwischen Linux und Windows


Nakai
2013-08-06, 19:17:15
Heyho,
meine Aufgabe ist es eine Software von Cygwin/Linux(beides 32bit) auf Windows umzusetzen. Die Software soll Gehirndaten segmentieren, weswegen auf viele FloatPoint-Ops zurückgegriffen wird. Derzeit wird die Software per Cygwin auf Windows ausgeführt. Die Ausführung auf Linux und Cygwin ist identisch, jedoch treten marginale Abweichungen zwischen Windows(Umsetzung in MinGW und per VisualStudio) und Linux auf.

Kurz:
Windows: VisualStudio-Neuimplementierung und MinGW liefern die gleichen Werte
Linux: Cygwin(unter Windows) und Nativ liefern die gleichen Werte

Es wird bei allen(falls nicht extra eingestellt) auf der X87-FPU ausgeführt(sieht man am ObjDmp).

Woran kann dies liegen? Führen die Linux-Libraries bestimmte Operationen anders aus? Compileroptimierungen ändern nichts an den Ergebnissen.
Hat da jemand etwas mehr Einblick?

Thunderhit
2013-08-06, 19:31:26
Irgendwelche besonderen Compilerflags bei GCC verwendet? Ansonsten such doch erstmal die Stelle bzw. Anweisung, ab der sich die Werte unterscheiden. Sonst ist das doch nur Rätselraten, was wir hier machen würden.

johla
2013-08-06, 19:36:00
Schau dir doch mal den Assembleroutput an.

Nakai
2013-08-06, 21:52:14
Im Originalprojekt wird -O3 verwendet. Dieses liefert jedoch keine unterschiedlichen Ergebnisse. Der Assemblercode wurde auch schon betrachtet und keine groben Unterschiede konnten festgemacht werden. Das liegt auch daran das der Tiefpassfilter anscheinend nur die Fehler dramatisch erhöht. Es ist ein höchst-iterativer Prozess, welcher die Fehler aufsummiert.
Die Fehler traten jedoch schon vorher auf. Der Fehler wurde auf einem Funktionsblock lokalisiert, welcher eine transzedente Funktion enthielt(exp()).

Ich gehe jetzt davon aus, dass diese Funktion den Fehler verursacht und auf den Linuxsystemen anders läuft. Nach näherem Untersuchen der Funktion im MinGW GCC-Compiler fehlt auf, dass diese Funktion in math.h definiert ist, welche wiederum so definiert ist:

_CRTIMP double __cdecl exp (double);

_CRTIMP ist ein Define für __declspec(dllimport), was eine microsoftspezifische DLL-Importierung ist. Kurz, es sieht so aus, als wenn dies ein Aufruf einer microsoftspezifischen mathematischen Funktion ist und anders die Funktion löst, mit teilweise minimalen Abweichungen. Das würde auch dazu passen, dass die VisualStudio-Version und die MinGW-Version identisch laufen und die Linux-Versionen(nativ, cygwin) ein anderes Ergebnis liefern.

Demirug
2013-08-06, 22:18:51
exp ist teil der Standard C math library. Der genaue Algorithmus bzw Berechnungsweg ist jedoch nicht vorgeschrieben. Ich glaube mich zu erinnern das Microsoft SSE benutzt. Möglicherweise benutzt die Linuxversion nur die normale FPU.

Wenn du keine Abweichungen tolerieren kannst musst du im Prinzip alle Funktionen der standard C math ibrary selbst implementieren oder fertigen Code dafür nehmen der sich von der Lizenz mit deinem anderen Verträgt.

Nakai
2013-08-07, 00:24:19
exp ist teil der Standard C math library. Der genaue Algorithmus bzw Berechnungsweg ist jedoch nicht vorgeschrieben. Ich glaube mich zu erinnern das Microsoft SSE benutzt. Möglicherweise benutzt die Linuxversion nur die normale FPU.


Laut Disassembly jedes Codes wird die X87-FPU verwendet. Alle verwenden X87-FPU-Code. Ich kanns auch mit SSE kompilieren, wenn ich will. Außerdem verwendet das 64bit-Linux-Kompilat standardgemäß SSE. Ein switchen auf X87 ist wohl nicht möglich.

Nakai
2013-08-07, 18:11:58
Erweiterung zu gestern. Ich habe nun den Assemblercode für exp() unter Cygwin und VisualStudio betrachtet. Dabei fiel mir auf, dass VS nciht durchgängig x87 verwendet, sondern in der exp()-Funktion auf SSE umschwenkt(laut disassembly, werden SSE-Funktionalitäten aufgerufen), während cygwin alles vollständig in X87 berechnet. Ich werde nun X87 vollständig fallen lassen und nun vollständig auf SSE umschwenken.

€: Also, die Unterschiede sind durch beidseitiges nutzen von SSE ziemlich gravierend gesenkt worden.

€2: @Demirug:
Du hattest vollkommen recht, mit deiner Aussage, btw.

joe kongo
2013-08-07, 21:22:09
Die Software soll Gehirndaten segmentieren.


klingt nach medizinischer Anwendung


Das liegt auch daran das der Tiefpassfilter anscheinend nur die Fehler dramatisch erhöht. Es ist ein höchst-iterativer Prozess, welcher die Fehler aufsummiert.


und das nach Kunstfehler

:freak:

Nakai
2013-08-08, 19:27:28
klingt nach medizinischer Anwendung

und das nach Kunstfehler

:freak:

Natürlich medizinisch, weswegen eine gewisse Genauigkeit schon notwendig ist.
Die Tiefpassfilterimplementierung(hab ich davor gemacht) in OpenCL erhöht die Fehler eh nochmal dramatisch, da ich keine Ahnung habe, was der OpenCL-Compiler ausspuckt.

Ahja, ich hab endlich den Linux-Code disassembliert um ihn genauer anzugucken. Die exp()-Funktion liegt für floats in ef_exp.c(für double e_exp.c) in der libm von GCC. Der Code ist vollkommen frei.

http://cygwin-ports.sourceforge.net/scan-build/newlib/report-bzt3l6.html (http://cygwin-ports.sourceforge.net/scan-build/newlib/report-
bzt3l6.html)

Dass ich nicht an die Microsoft-Implementierung rankomme ist ein Witz. Unter Linux kann ich wenigstens die Implementierung angucken und wahrscheinlich wird eh immer die Funktion korrekt kompiliert, unter Windows wirds wohl einfach auf die eigene LibM gelinkt und SSE benutzt(Mischcode).

Ahja, ich merk ja selber, dass die Anteilnahme eh nicht so groß ist, was nicht verwundert, da das Thema etwas spezieller ist. Ich schreib hier trotzdem einfach weiter, weil es womöglich den einen oder anderen interessiert.


mfg

Gast
2013-08-09, 07:37:50
Ich finde es interessant.

Kannst du einmal genauer sagen, was du verwendest und was du brauchst? Was hat z.B. cygwin mit mingw zu tun? Hast du 64 oder 32 Bit Windows? Das Flag für gcc, um FP87 einzuschalten lautet -mfpmath=387 und ist unter Windows 64 Bit z.B. standardmäßig nicht aktiv.

Nakai
2013-08-09, 13:33:26
Ich finde die Beschreibung von Cygwin und MinGW im Internet etwas verwirrend, ich werde es versuchen in eigene Worte zu fassen.

Cygwin versucht die gesamte Linux/Unix/Posix-Umgebung unter Windows zu imitieren. Das bedeutet, dass konvertierte DLLs/Bibliotheken verwendet werden. Als Compiler wird GCC verwendet.

MinGW benutzt auch GCC, jedoch wird die gesamte Umgebung(Linux/Unix/Posix) nicht verwendet. Es sind zwar immer noch sehr viele Sourcen dabei, aber bei vielen Bibliotheken wird auf das Microsoft Pendant zugegriffen.

Ich weiß nicht, ob man das so beschreiben kann und ob es überhaupt richtig ist.
MinGW(mit Codeblocks) und MVS nutzen vorkompilierte Bibliotheken, während Cygwin die Bibliotheken direkt reinkompiliert(kurz ich kann mir die Implementierung genau angucken). Das ist gefährlich, da die Microsoft LibM standardgemäß SSE verwenden, während die Cygwin-Version von den Compilerversionen abhängig ist. Hierzu habe ich nun eine eigene Version einiger LibM-Funktionen zusammengestellt, welche in mein VS-Projekt reinkompiliert werden. Ich habe die exp()-Funktion von Cygwin auf Windows portiert, was nicht schwer war. Die Implementierung verursacht Unterschiede.

Ich habe derzeit Einstellungen gefunden, welche in "Ordnung" sind.
MSV nutzt fp-model:strict, 32bit-X87, keine Optimierungen.
Cygwin nutzt 64bit-X87.
Beides liegt sehr nah beeinander, die Abweichungen zwischen beiden Implementierungen sind wohl <=2ulp(und auch nur sehr selten).

Ich weiß wirklich nicht woran das liegt, es sieht jedoch so aus, dass die Compiler durch die Kompilation Unterschiede verursachen. Ich poste später mal das Disassembly.