Wie viel besser sind Fortran-Compiler wirklich?

74

Diese Frage ist eine Erweiterung von zwei Diskussionen, die kürzlich in den Antworten zu " C ++ vs Fortran for HPC " aufgetaucht sind . Und es ist eher eine Herausforderung als eine Frage ...

Eines der am häufigsten gehörten Argumente für Fortran ist, dass die Compiler einfach besser sind. Da die meisten C / Fortran-Compiler dasselbe Back-End verwenden, sollte der für semantisch äquivalente Programme in beiden Sprachen generierte Code identisch sein. Man könnte jedoch argumentieren, dass C / Fortran für den Compiler mehr oder weniger einfacher zu optimieren ist.

Also habe ich mich für einen einfachen Test entschieden: Ich habe eine Kopie von daxpy.f und daxpy.c bekommen und sie mit gfortran / gcc kompiliert.

Jetzt ist daxpy.c nur eine f2c-Übersetzung von daxpy.f (automatisch generierter Code, hässlich wie zum Teufel), also habe ich diesen Code genommen und ein bisschen aufgeräumt (meet daxpy_c), was im Grunde bedeutete, die innerste Schleife neu zu schreiben als

for ( i = 0 ; i < n ; i++ )
    dy[i] += da * dx[i];

Schließlich habe ich es neu geschrieben (geben Sie daxpy_cvec ein) und dabei die Vektorsyntax von gcc verwendet:

#define vector(elcount, type)  __attribute__((vector_size((elcount)*sizeof(type)))) type
vector(2,double) va = { da , da }, *vx, *vy;

vx = (void *)dx; vy = (void *)dy;
for ( i = 0 ; i < (n/2 & ~1) ; i += 2 ) {
    vy[i] += va * vx[i];
    vy[i+1] += va * vx[i+1];
    }
for ( i = n & ~3 ; i < n ; i++ )
    dy[i] += da * dx[i];

Beachten Sie, dass ich Vektoren der Länge 2 verwende (das ist alles, was SSE2 zulässt) und zwei Vektoren gleichzeitig verarbeite. Dies liegt daran, dass wir auf vielen Architekturen möglicherweise mehr Multiplikationseinheiten als Vektorelemente haben.

Alle Codes wurden mit gfortran / gcc Version 4.5 mit den Flags "-O3 -Wall -msse2 -march = native -ffast-math -fomit-frame-pointer -malign-double -fstrict-aliasing" kompiliert. Auf meinem Laptop (Intel Core i5 CPU, M560, 2,67 GHz) habe ich folgende Ausgabe erhalten:

pedro@laika:~/work/fvsc$ ./test 1000000 10000
timing 1000000 runs with a vector of length 10000.
daxpy_f took 8156.7 ms.
daxpy_f2c took 10568.1 ms.
daxpy_c took 7912.8 ms.
daxpy_cvec took 5670.8 ms.

Der ursprüngliche Fortran-Code benötigt also etwas mehr als 8,1 Sekunden, die automatische Übersetzung 10,5 Sekunden, die naive C-Implementierung 7,9 und der explizit vektorisierte Code 5,6, etwas weniger.

Das heißt, Fortran ist etwas langsamer als die naive C-Implementierung und 50% langsamer als die vektorisierte C-Implementierung.

Hier ist die Frage: Ich bin ein gebürtiger C-Programmierer und daher ziemlich zuversichtlich, dass ich an diesem Code gute Arbeit geleistet habe, aber der Fortran-Code wurde zuletzt 1993 überarbeitet und ist möglicherweise etwas veraltet. Kann irgendjemand einen besseren Job machen, dh wettbewerbsfähiger als eine der beiden C-Versionen, da ich mich in Fortran nicht so wohl fühle wie andere hier?

Kann jemand diesen Test auch mit icc / ifort ausprobieren? Die Vektorsyntax wird wahrscheinlich nicht funktionieren, aber ich wäre gespannt, wie sich die naive C-Version dort verhält. Gleiches gilt für alle mit xlc / xlf herumliegen.

Ich habe die Quellen und ein Makefile hier hochgeladen . Um genaue Timings zu erhalten, setzen Sie CPU_TPS in test.c auf die Anzahl der Hz in Ihrer CPU. Wenn Sie Verbesserungen an einer der Versionen finden, posten Sie diese bitte hier!

Aktualisieren:

Ich habe den Online-Dateien den Testcode von stali hinzugefügt und ihn mit einer C-Version ergänzt. Ich habe die Programme dahingehend modifiziert, dass 1'000'000 Schleifen auf Vektoren der Länge 10'000 ausgeführt werden, um mit dem vorherigen Test übereinzustimmen (und weil meine Maschine keine Vektoren der Länge 1'000'000'000 zuweisen konnte, wie im Original von stali Code). Da die Zahlen jetzt etwas kleiner sind, habe ich die Option verwendet -par-threshold:50, um die Wahrscheinlichkeit der Parallelisierung des Compilers zu erhöhen. Die verwendete icc / ifort Version ist 12.1.2 20111128 und die Ergebnisse sind wie folgt

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./icctest_c
3.27user 0.00system 0:03.27elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./icctest_f
3.29user 0.00system 0:03.29elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./icctest_c
4.89user 0.00system 0:02.60elapsed 188%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./icctest_f
4.91user 0.00system 0:02.60elapsed 188%CPU

Zusammenfassend sind die Ergebnisse für alle praktischen Zwecke sowohl für die C- als auch für die Fortran-Version identisch, und beide Codes werden automatisch parallelisiert. Beachten Sie, dass die schnellen Zeiten im Vergleich zum vorherigen Test auf die Verwendung von Gleitkomma-Arithmetik mit einfacher Genauigkeit zurückzuführen sind!

Aktualisieren:

Obwohl mir die Beweislast hier nicht wirklich gefällt, habe ich das Matrixmultiplikationsbeispiel von stali in C neu codiert und den Dateien im Web hinzugefügt . Hier sind die Ergebnisse der Tripple-Schleife für eine und zwei CPUs:

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 2500
 triple do time   3.46421700000000     
3.63user 0.06system 0:03.70elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_c 2500
triple do time 3.431997791385768
3.58user 0.10system 0:03.69elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 2500
 triple do time   5.09631900000000     
5.26user 0.06system 0:02.81elapsed 189%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_c 2500
triple do time 2.298916975280899
4.78user 0.08system 0:02.62elapsed 184%CPU

Beachten Sie, dass cpu_timein Fortran die CPU-Zeit und nicht die Wanduhr-Zeit gemessen wird. Deshalb habe ich die Aufrufe eingepackt time, um sie für 2 CPUs zu vergleichen. Es gibt keinen wirklichen Unterschied zwischen den Ergebnissen, außer dass die C-Version auf zwei Kernen etwas besser abschneidet.

Nun zum matmulBefehl, natürlich nur in Fortran, da diese Eigenschaft in C nicht verfügbar ist:

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 2500
 matmul    time   23.6494780000000     
23.80user 0.08system 0:23.91elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 2500
 matmul    time   26.6176640000000     
26.75user 0.10system 0:13.62elapsed 197%CPU

Beeindruckend. Das ist absolut schrecklich. Kann jemand herausfinden, was ich falsch mache, oder erklären, warum dies irgendwie immer noch eine gute Sache ist?

Ich habe die dgemmAufrufe nicht zum Benchmark hinzugefügt , da es sich um Bibliotheksaufrufe für dieselbe Funktion in der Intel MKL handelt.

Kann jemand für zukünftige Tests ein Beispiel vorschlagen, von dem bekannt ist , dass es in C langsamer ist als in Fortran?

Aktualisieren

Um die Behauptung von stali zu bestätigen, dass das matmulIntrinsic "eine Größenordnung" schneller ist als das explizite Matrixprodukt auf kleineren Matrizen, habe ich seinen eigenen Code modifiziert, um Matrizen der Größe 100x100 mit beiden Methoden zu multiplizieren, jeweils 10'000-mal. Die Ergebnisse für eine und zwei CPUs lauten wie folgt:

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 10000 100
 matmul    time   3.61222500000000     
 triple do time   3.54022200000000     
7.15user 0.00system 0:07.16elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 10000 100
 matmul    time   4.54428400000000     
 triple do time   4.31626900000000     
8.86user 0.00system 0:04.60elapsed 192%CPU

Aktualisieren

Grisu weist zu Recht darauf hin, dass gcc ohne Optimierungen Operationen auf komplexen Zahlen in Bibliotheksfunktionsaufrufe umwandelt, während gfortran sie in ein paar Anweisungen einfügt.

Der C-Compiler generiert den gleichen, kompakten Code, wenn die Option -fcx-limited-rangegesetzt ist, dh der Compiler wird angewiesen, mögliche Über- / Unterläufe in den Zwischenwerten zu ignorieren. Diese Option ist in gfortran standardmäßig aktiviert und kann zu falschen Ergebnissen führen. Das Erzwingen -fno-cx-limited-rangevon Gfortran änderte nichts.

Dies ist also eigentlich ein Argument gegen die Verwendung von Gfortran für numerische Berechnungen: Operationen mit komplexen Werten können über- / unterlaufen, selbst wenn die korrekten Ergebnisse im Gleitkommabereich liegen. Dies ist eigentlich ein Fortran-Standard. In gcc oder in C99 im Allgemeinen ist die Standardeinstellung, dass die Dinge streng ausgeführt werden (lesen Sie IEEE-754-konform), sofern nicht anders angegeben.

Erinnerung: Bitte beachten Sie, dass die Hauptfrage lautete, ob Fortran-Compiler besseren Code als C-Compiler produzieren. Dies ist nicht der Ort für Diskussionen über die allgemeinen Vorzüge einer Sprache gegenüber einer anderen. Was mich wirklich interessiert, ist, ob irgendjemand einen Weg finden kann, Gfortran zu überreden, ein so effizientes Daxpy wie das in C unter Verwendung expliziter Vektorisierung zu erzeugen, da dies die Probleme veranschaulicht, sich ausschließlich für die SIMD-Optimierung auf den Compiler verlassen zu müssen, oder a Fall, in dem ein Fortran-Compiler sein C-Gegenstück übertrifft.

Pedro
quelle
Ein Zeitproblem ist, dass, wenn Ihr Prozessor den Frequenzsprung- / Turbomodus ausführt, diese Ergebnisse auf der gesamten Karte angezeigt werden können.
Bill Barth
1
Ihre daxpy_c.c aktualisiert derzeit x mit einem Vielfachen von x und berührt y überhaupt nicht. Vielleicht möchten Sie das beheben, um es fair zu machen ...
Jack Poulson
1
@ JackPoulson: Guter Fang, korrigiert und aktualisiert die Ergebnisse.
Pedro
2
Ich bin mir auch ziemlich sicher, dass der Unterschied vollständig auf das manuelle Entrollen in der Fortran-Version zurückzuführen ist, das den Compiler verwirrt. Wenn ich es durch dieselbe einfache Schleife ersetze, die Sie in Ihre C-Version eingefügt haben, ist die Leistung zwischen den beiden fast identisch. Ohne die Änderung war die Fortran-Version mit den Intel-Compilern langsamer.
Jack Poulson
1
@permeakra: Tatsächlich gibt der C99-Standard das restrictSchlüsselwort an, das dem Compiler genau sagt, dass: angenommen wird, dass sich ein Array nicht mit einer anderen Datenstruktur überschneidet.
Pedro

Antworten:

37

Der Unterschied in Ihrem Timing scheint auf das manuelle Abrollen des Fortran daxpy zurückzuführen zu sein . Die folgenden Timings beziehen sich auf einen 2,67-GHz-Xeon X5650, der den Befehl verwendet

./test 1000000 10000

Intel 11.1-Compiler

Fortran mit manuellem Abrollen: 8,7 Sek.
Fortran ohne manuelles Abrollen: 5,8 Sek.
C ohne manuelles Abrollen: 5,8 Sek

GNU 4.1.2-Compiler

Fortran mit manuellem Abrollen: 8,3 Sek.
Fortran ohne manuelles Abrollen: 13,5 Sek.
C ohne manuelles Abrollen: 13,6 Sek.
C mit Vektorattributen: 5,8 Sek

GNU 4.4.5-Compiler

Fortran mit manuellem Abrollen: 8,1 Sek.
Fortran ohne manuelles Abrollen: 7,4 Sek.
C ohne manuelles Abrollen: 8,5 Sek.
C mit Vektorattributen: 5,8 Sek

Schlussfolgerungen

  • Das manuelle Entrollen half den GNU 4.1.2 Fortran-Compilern bei dieser Architektur, schadete jedoch der neueren Version (4.4.5) und dem Intel Fortran-Compiler.
  • Der GNU 4.4.5 C-Compiler ist mit Fortran wesentlich wettbewerbsfähiger als mit Version 4.2.1.
  • Mithilfe von Vektor-Intrinsics kann die GCC-Leistung an die Intel-Compiler angepasst werden.

Zeit, kompliziertere Routinen wie dgemv und dgemm zu testen?

Jack Poulson
quelle
Danke für die Ergebnisse! Welche Version von gcc haben Sie verwendet und können Sie etwas spezifischer in Bezug auf die CPU sein?
Pedro
2
Ihr Compiler ist älter als Ihre CPU ... Können Sie es mit gcc-4.5 versuchen?
Pedro
1
Ich habe es einfach versucht. Die vektorisierte Version mit GCC 4.4.5 entspricht exakt den Ergebnissen von Intel 11.1.
Jack Poulson
1
Ich habe gerade gcc / gfortran Version 4.4.5 installiert und kann die Unterschiede nicht reproduzieren, ohne sie zu entrollen. Tatsächlich ist in dem Assembler, der für beide Fälle generiert wurde, die innerste Schleife identisch, mit Ausnahme der verwendeten Registernamen, die austauschbar sind. Können Sie Ihre Tests erneut durchführen, um sicherzugehen?
Pedro
4
Können wir sagen, dass auf diese Weise die uralte Debatte beigelegt wird, dass wir Fortran weiterhin verwenden, weil es leistungsfähiger ist, damit wir es endlich in den Müllcontainer werfen können?
Stefano Borini
16

Ich komme zu spät zu dieser Party, daher fällt es mir schwer, das Hin und Her von oben zu verfolgen. Die Frage ist groß und ich denke, wenn Sie interessiert sind, könnte sie in kleinere Teile zerlegt werden. Eine Sache, die mich interessierte, war einfach die Leistung Ihrer daxpyVarianten und ob Fortran in diesem sehr einfachen Code langsamer als C ist.

Unter meinem Laptop (Macbook Pro, Intel Core i7, 2,66 GHz) hängt die relative Leistung Ihrer handvektorisierten C-Version und der nicht handvektorisierten Fortran-Version vom verwendeten Compiler ab (mit Ihren eigenen Optionen):

Compiler     Fortran time     C time
GCC 4.6.1    5408.5 ms        5424.0 ms
GCC 4.5.3    7889.2 ms        5532.3 ms
GCC 4.4.6    7735.2 ms        5468.7 ms

Es scheint also, dass GCC die Schleife in der 4.6-Verzweigung besser vektorisieren konnte als zuvor.


In der Gesamtdebatte kann man, wie in der Assemblersprache, ziemlich schnell und optimierten Code sowohl in C als auch in Fortran schreiben. Ich möchte jedoch auf eine Sache hinweisen: So wie Assembler mühsamer zu schreiben ist als C, aber Ihnen eine genauere Kontrolle darüber gibt, was von der CPU ausgeführt wird, ist C niedriger als Fortran. Auf diese Weise haben Sie mehr Kontrolle über die Details. Dies kann bei der Optimierung hilfreich sein, wenn die Fortran-Standardsyntax (oder ihre Herstellererweiterungen) möglicherweise nicht über ausreichende Funktionen verfügt. Ein Fall ist die explizite Verwendung von Vektortypen, ein anderer ist die Möglichkeit, die Ausrichtung von Variablen von Hand festzulegen, wozu Fortran nicht in der Lage ist.

F'x
quelle
Willkommen bei Scicomp! Ich bin damit einverstanden, dass Compilerversionen in diesem Fall genauso wichtig sind wie die Sprache. Meinten Sie in Ihrem letzten Satz "von" statt "aus"?
Aron Ahmadia
9

Die Art und Weise, wie ich AXPY in Fortran schreiben würde, ist etwas anders. Es ist die genaue Übersetzung der Mathematik.

m_blas.f90

 module blas

   interface axpy
     module procedure saxpy,daxpy
   end interface

 contains

   subroutine daxpy(x,y,a)
     implicit none
     real(8) :: x(:),y(:),a
     y=a*x+y
   end subroutine daxpy

   subroutine saxpy(x,y,a)
     implicit none
     real(4) :: x(:),y(:),a
     y=a*x+y
   end subroutine saxpy

 end module blas

Rufen wir nun die obige Routine in einem Programm auf.

test.f90

 program main

   use blas
   implicit none

   real(4), allocatable :: x(:),y(:)
   real(4) :: a
   integer :: n

   n=1000000000
   allocate(x(n),y(n))
   x=1.0
   y=2.0
   a=5.0
   call axpy(x,y,a)
   deallocate(x,y)

 end program main

Jetzt kompilieren wir es und führen es aus ...

login1$ ifort -fast -parallel m_blas.f90 test.f90
ipo: remark #11000: performing multi-file optimizations
ipo: remark #11005: generating object file /tmp/ipo_iforttdqZSA.o

login1$ export OMP_NUM_THREADS=1
login1$ time ./a.out 
real    0 m 4.697 s
user    0 m 1.972 s
sys     0 m 2.548 s

login1$ export OMP_NUM_THREADS=2
login1$ time ./a.out 
real    0 m 2.657 s
user    0 m 2.060 s
sys     0 m 2.744 s

Beachten Sie, dass ich keine Schleifen oder expliziten OpenMP- Anweisungen verwende. Wäre dies in C möglich (dh keine Verwendung von Schleifen und automatische Parallelisierung)? Ich benutze kein C, also weiß ich es nicht.

stali
quelle
Die automatische Parallelisierung ist ein Merkmal der Intel-Compiler (Fortran und C) und nicht der Sprache. Daher sollte das Äquivalent in C auch parallelisieren. Wie verhält es sich aus Neugier für ein moderateres n = 10000?
Pedro
3
Das war der springende Punkt. Autopar ist in Fortran einfacher, da Fortran (im Gegensatz zu C) ganze Array-Operationen wie Matmult, Transponieren usw. unterstützt. Daher ist die Code-Optimierung für Fortran-Compiler einfacher. GFortran (das Sie verwendet haben) verfügt nicht über die Entwicklerressourcen, um den Fortran-Compiler zu optimieren, da der Schwerpunkt derzeit auf der Implementierung des Fortran 2003-Standards und nicht auf der Optimierung liegt.
Stali
Uhmm ... Der Intel C / C ++ - Compiler führt iccauch eine automatische Parallelisierung durch. Ich habe eine Datei icctest.czu den anderen Quellen hinzugefügt . Können Sie es mit denselben Optionen wie oben kompilieren, ausführen und die Zeitangaben protokollieren? Ich musste meinem Code eine printf-Anweisung hinzufügen, um zu vermeiden, dass gcc alles optimiert. Dies ist nur ein kurzer Hack und ich hoffe er ist fehlerfrei!
Pedro
Ich habe die neuesten icc / ifort-Compiler heruntergeladen und die Tests selbst durchgeführt. Die Frage wurde aktualisiert, um diese neuen Ergebnisse einzubeziehen, dh, Intels Autovektorisierung funktioniert sowohl in Fortran als auch in C.
Pedro
1
Vielen Dank. Ja, mir ist aufgefallen, dass es vielleicht kaum einen Unterschied gibt, weil die Schleifen einfach sind und die Operationen Stufe 1 BLAS sind. Wie ich bereits sagte, gibt es aufgrund der Fähigkeit von Fortran, ganze Array-Operationen und die Verwendung von Schlüsselwörtern wie PURE / ELEMENTAL durchzuführen, mehr Raum für Compiler-Optimierung. Wie die Compiler diese Informationen verwenden und was sie wirklich tun, ist eine andere Sache. Sie können auch matmul ausprobieren, wenn Sie möchten, dass bpaste.net/show/23035
stali
6

Ich finde es nicht nur interessant, wie ein Compiler den Code für moderne Hardware optimiert. Insbesondere zwischen GNU C und GNU Fortran kann die Codegenerierung sehr unterschiedlich sein.

Betrachten wir also ein anderes Beispiel, um die Unterschiede zwischen ihnen zu zeigen.

Unter Verwendung komplexer Zahlen erzeugt der GNU C-Compiler einen großen Overhead für nahezu grundlegende arithmetische Operationen mit komplexen Zahlen. Der Fortran-Compiler liefert viel besseren Code. Schauen wir uns das folgende kleine Beispiel in Fortran an:

COMPLEX*16 A,B,C
C=A*B

ergibt (gfortran -g -o complex.fo -c complex.f95; objdump -d -S complex.fo):

C=A*B
  52:   dd 45 e0                fldl   -0x20(%ebp)
  55:   dd 45 e8                fldl   -0x18(%ebp)
  58:   dd 45 d0                fldl   -0x30(%ebp)
  5b:   dd 45 d8                fldl   -0x28(%ebp)
  5e:   d9 c3                   fld    %st(3)
  60:   d8 ca                   fmul   %st(2),%st
  62:   d9 c3                   fld    %st(3)
  64:   d8 ca                   fmul   %st(2),%st
  66:   d9 ca                   fxch   %st(2)
  68:   de cd                   fmulp  %st,%st(5)
  6a:   d9 ca                   fxch   %st(2)
  6c:   de cb                   fmulp  %st,%st(3)
  6e:   de e9                   fsubrp %st,%st(1)
  70:   d9 c9                   fxch   %st(1)
  72:   de c2                   faddp  %st,%st(2)
  74:   dd 5d c0                fstpl  -0x40(%ebp)
  77:   dd 5d c8                fstpl  -0x38(%ebp)

Welches sind 39 Bytes Maschinencode. Wenn wir das gleiche in C betrachten

 double complex a,b,c; 
 c=a*b; 

und werfen Sie einen Blick auf die Ausgabe (auf die gleiche Weise wie oben), erhalten wir:

  41:   8d 45 b8                lea    -0x48(%ebp),%eax
  44:   dd 5c 24 1c             fstpl  0x1c(%esp)
  48:   dd 5c 24 14             fstpl  0x14(%esp)
  4c:   dd 5c 24 0c             fstpl  0xc(%esp)
  50:   dd 5c 24 04             fstpl  0x4(%esp)
  54:   89 04 24                mov    %eax,(%esp)
  57:   e8 fc ff ff ff          call   58 <main+0x58>
  5c:   83 ec 04                sub    $0x4,%esp
  5f:   dd 45 b8                fldl   -0x48(%ebp)
  62:   dd 5d c8                fstpl  -0x38(%ebp)
  65:   dd 45 c0                fldl   -0x40(%ebp)
  68:   dd 5d d0                fstpl  -0x30(%ebp)

Der 39-Byte-Maschinencode, auf den sich jedoch der Funktionsschritt 57 bezieht, erledigt den richtigen Teil der Arbeit und führt die gewünschte Operation aus. Wir haben also 27-Byte-Maschinencode, um die Mehrfachoperation auszuführen. Die dahinter stehende Funktion ist muldc3 von libgcc_s.sound hat eine Grundfläche von 1375 Byte im Maschinencode. Dies verlangsamt den Code drastisch und liefert bei Verwendung eines Profilers eine interessante Ausgabe.

Wenn wir die obigen BLAS-Beispiele für zaxpydenselben Test implementieren und ausführen, sollte der Fortran-Compiler bessere Ergebnisse liefern als der C-Compiler.

(Für dieses Experiment habe ich GCC 4.4.3 verwendet, aber ich habe dieses Verhalten bei einem anderen GCC-Release bemerkt.)

Meiner Meinung nach denken wir also nicht nur an Parallelisierung und Vektorisierung, wenn wir uns überlegen, welcher Compiler der bessere ist, sondern auch, wie grundlegende Dinge in Assembler-Code übersetzt werden. Wenn diese Übersetzung fehlerhaften Code ergibt, kann die Optimierung diese Dinge nur als Eingabe verwenden.

MK aka Grisu
quelle
1
Ich habe gerade ein Beispiel in Anlehnung an Ihren Code erfunden complex.cund es online zum Code hinzugefügt. Ich musste alle Ein- / Ausgaben hinzufügen, um sicherzustellen, dass nichts optimiert ist. Ich werde nur angerufen, __muldc3wenn ich nicht benutze -ffast-math. Mit -O2 -ffast-mathbekomme ich 9 Zeilen inline Assembler. Kannst du das bestätigen?
Pedro
Ich habe eine spezifischere Ursache für den Unterschied im generierten Assembler gefunden und dies meiner obigen Frage hinzugefügt.
Pedro
Die Verwendung von -O2 veranlasst den Compiler, alles Mögliche zur Laufzeit zu berechnen. Deshalb gehen solche Konstrukte manchmal verloren. Die Option -ffast-math sollte beim wissenschaftlichen Rechnen nicht verwendet werden, wenn Sie sich auf die Ausgaben verlassen möchten.
MK aka Grisu
1
Nun, mit diesem Argument (nein -ffast-math) sollten Sie Fortran nicht für Ihre komplexwertigen Berechnungen verwenden. Wie ich im Update zu meiner Frage beschreibe, -ffast-mathoder allgemeiner, -fcx-limited-rangezwingt gcc dazu, dieselben Nicht-IEEE-Berechnungen mit eingeschränktem Bereich zu verwenden, wie sie in Fortran Standard sind . Wenn Sie also den gesamten Bereich komplexer Werte und korrekter Infs und NaNs benötigen, sollten Sie Fortran nicht verwenden ...
Pedro
2
@Pedro: Wenn du willst, dass GCC sich wie GFortran verhält. Für komplexe Multiplikationen und Divisionen sollten Sie die -fcx-fortran-Regeln verwenden.
2.
4

Leute,

Ich fand diese Diskussion sehr interessant, war aber überrascht zu sehen, dass die Neuordnung der Loops im Matmul-Beispiel das Bild veränderte. Ich habe keinen Intel-Compiler auf meinem aktuellen Rechner, daher verwende ich gfortran, sondern schreibe die Schleifen in der mm_test.f90 um

call cpu_time(start)  
do r=1,runs  
  mat_c=0.0d0  
     do j=1,n  
        do k=1,n  
  do i=1,n  
           mat_c(i,j)=mat_c(i,j)+mat_a(i,k)*mat_b(k,j)  
        end do  
     end do  
  end do  
end do  
call cpu_time(finish)  

änderte die gesamten Ergebnisse für meine Maschine.

Die Timing-Ergebnisse der vorherigen Version waren:

#time ./mm_test_f 10000 100
 matmul    time   6.3620000000000001     
 triple do time   21.420999999999999     

wohingegen mit den wie oben geordneten Dreifachschleifen:

#time ./mm_test_f 10000 100
 matmul    time   6.3929999999999998     
 triple do time   3.9190000000000005    

Dies ist gcc / gfortran 4.7.2 20121109 auf einer Intel (R) Core (TM) i7-2600K-CPU mit 3,40 GHz

Es wurden Compiler-Flags aus dem Makefile verwendet, das ich hier bekommen habe ...

Schatzi
quelle
3
Dies ist nicht verwunderlich, da der Matrixspeicher eine Reihenfolge bevorzugt, dh, wenn Zeilen zusammenhängend gespeichert werden, ist es besser, die innersten Zeilen zu durchlaufen, da Sie dann jede Zeile einmal in den schnellen lokalen Speicher laden können, anstatt sie wiederholt zu laden (ein Teil von ), um auf ein einzelnes Element zuzugreifen. Siehe stackoverflow.com/questions/7395556 .
Christian Clason
Ich schätze, ich war überrascht, dass das "intrinsische Matmul" nicht so codiert werden würde, dass es die Dinge so macht. Mit dem auf die zweite Weise bestellten Triple-Do geht es wesentlich schneller. Es scheint in diesem Compiler-Set zu sein, da frühere Gfortran-Versionen, die ich bekommen kann, in ihrem Timing "flacher" waren - es war egal, auf welche Weise Sie die Multis gemacht haben - es dauerte fast dieselbe Zeit.
Schatzi
-2

Es sind keine Sprachen, die den Code schneller laufen lassen, obwohl sie helfen. Es sind der Compiler, die CPU und das Betriebssystem, die die Ausführung von Codes beschleunigen. Der Vergleich von Sprachen ist nur eine Fehlbezeichnung, nutzlos und bedeutungslos. Es macht überhaupt keinen Sinn, weil Sie zwei Variablen vergleichen: die Sprache und den Compiler. Wenn ein Code schneller ausgeführt wird, wissen Sie nicht, wie viel die Sprache oder wie viel der Compiler ist. Ich verstehe nicht, warum die Informatik-Community das einfach nicht versteht :-(

Wadud Miah
quelle