Als ich etwas Fortran-Code nach C portierte, überraschte es mich, dass der größte Unterschied in der Ausführungszeit zwischen dem mit ifort (Intel Fortran-Compiler) kompilierten Fortran-Programm und dem mit gcc kompilierten c-Programm von den Auswertungen trigonometrischer Funktionen herrührt (sin
, cos
). Es hat mich überrascht, weil ich früher geglaubt habe, was diese Antwort erklärt, dass Funktionen wie Sinus und Cosinus in Mikrocode in Mikroprozessoren implementiert sind.
Um das Problem deutlicher zu erkennen habe ich ein kleines Testprogramm in Fortran erstellt
program ftest
implicit none
real(8) :: x
integer :: i
x = 0d0
do i = 1, 10000000
x = cos (2d0 * x)
end do
write (*,*) x
end program ftest
An intel Q6600
Prozessor u 3.6.9-1-ARCH x86_64 Linux
Ich komme mit ifort version 12.1.0
$ ifort -o ftest ftest.f90
$ time ./ftest
-0.211417093282753
real 0m0.280s
user 0m0.273s
sys 0m0.003s
während mit gcc version 4.7.2
Ich bekomme
$ gfortran -o ftest ftest.f90
$ time ./ftest
0.16184945593939115
real 0m2.148s
user 0m2.090s
sys 0m0.003s
Das ist fast ein Faktor 10 Unterschied! Kann ich noch glauben, dass die gcc-Implementierung von cos
ist ein Wrapper um die Mikroprozessorimplementierung in ähnlicher Weise wie dies wahrscheinlich in der Intel-Implementierung erfolgt? Wenn das stimmt, wo ist der Flaschenhals?
BEARBEITEN
Laut Kommentaren sollten aktivierte Optimierungen die Leistung verbessern. Meine Meinung war, dass Optimierungen die Bibliotheksfunktionen nicht beeinflussen … was nicht bedeutet, dass ich sie nicht in nicht trivialen Programmen verwende. Hier sind jedoch zwei zusätzliche Benchmarks (jetzt auf meinem Heimcomputer intel core2
)
$ gfortran -o ftest ftest.f90
$ time ./ftest
0.16184945593939115
real 0m2.993s
user 0m2.986s
sys 0m0.000s
und
$ gfortran -Ofast -march=native -o ftest ftest.f90
$ time ./ftest
0.16184945593939115
real 0m2.967s
user 0m2.960s
sys 0m0.003s
Welche speziellen Optimierungen hatten Sie (Kommentatoren) im Sinn? Und wie kann der Compiler in diesem speziellen Beispiel einen Mehrkernprozessor ausnutzen, bei dem jede Iteration vom Ergebnis der vorherigen abhängt?
BEARBEITEN 2
Die Benchmark-Tests von Daniel Fisher und Ilmari Karonen ließen mich denken, dass das Problem mit der bestimmten Version von gcc (4.7.2) und vielleicht mit einem bestimmten Build davon (Arch x86_64 Linux) zusammenhängt, den ich auf meinen Computern verwende. Also wiederholte ich den Test auf der intel core i7
Kiste mit debian x86_64 Linux
, gcc version 4.4.5
und ifort version 12.1.0
$ gfortran -O3 -o ftest ftest.f90
$ time ./ftest
0.16184945593939115
real 0m0.272s
user 0m0.268s
sys 0m0.004s
und
$ ifort -O3 -o ftest ftest.f90
$ time ./ftest
-0.211417093282753
real 0m0.178s
user 0m0.176s
sys 0m0.004s
Für mich ist dies ein sehr akzeptabler Leistungsunterschied, der mich niemals dazu bringen würde, diese Frage zu stellen. Es scheint, dass ich in Arch Linux-Foren nach diesem Problem fragen muss.
Die Erklärung der ganzen Geschichte ist aber trotzdem sehr willkommen.
Warum ist der berechnete Wert unterschiedlich?
– Ausruhen
14. Dezember 2012 um 11:56 Uhr
Es ist näher an x8, haben Sie eine Maschine mit 8 Kernen? Auto-Vektorisierung sollte auch eine Rolle spielen, aber das haben Sie deaktiviert. Das Benchmarking von Code ohne Einschalten des Optimierers ist Zeitverschwendung.
– Hans Passant
14. Dezember 2012 um 12:04 Uhr
+1: Mit Optimierung kompilieren oder weggehen.
– ams
14. Dezember 2012 um 12:29 Uhr
Der Grund, warum die Optimierung wichtig gewesen sein könnte, ist, dass gcc bei der Optimierung manchmal Bibliotheksaufrufe durch andere Dinge (entweder andere Bibliotheksaufrufe oder Inline-Code) ersetzt.
– Wels_Man
14. Dezember 2012 um 19:20 Uhr
@Digikata,
ifort
führt keinerlei Schleifenentrollung durch. Es übersetzt die Schleife sehr wörtlich und ruft in seine eigene optimierte aufcos
Routine, die es irgendwie schafft, eine schnellere Cosinus-Berechnung durchzuführen alsfcos
. Auf den ersten Blick das zerlegtecos
Die Routine sieht aus wie die Verwendung einer tabellarischen Argumentreduktion. GCC ruft die systemweite Mathematikbibliothek auf, die etwas Ähnliches implementiert, aber nicht so optimiert für Intel-Prozessoren wielibimf
tut.– Christo Iljew
14. Dezember 2012 um 23:06 Uhr