Generieren Fortran-Compiler wirklich schnelleren Code als C-Compiler?

17

Als ich an der Universität studierte, hörte ich oft die Idee, dass Fortran-Compiler für ein gleichwertiges Programm schnelleren Code erzeugten als C-Compiler.

Die wichtigste Überlegung lautete wie folgt : Ein Fortran-Compiler gibt durchschnittlich 1,1 Prozessoranweisungen pro Codezeile aus, während ein C-Compiler durchschnittlich 1,6 Prozessoranweisungen pro Codezeile ausgibt - ich erinnere mich nicht an die genauen Zahlen, aber an die Die Idee war, dass C-Compiler merklich mehr Maschinencode ausgaben und daher langsamere Programme produzierten.

Wie gültig ist ein solcher Vergleich? Können wir sagen, dass Fortran-Compiler schnellere Programme produzieren als C-Compiler oder umgekehrt und warum gibt es diesen Unterschied?

scharfer Zahn
quelle
19
Das kann einfach bedeuten, dass Fortran-Programme ausführlicher sind als C ... Ein aussagekräftiger Vergleich kann nur durchgeführt werden, wenn in beiden Sprachen dieselbe Funktionalität implementiert und der resultierende Maschinencode (Größe und Geschwindigkeit) verglichen wird.
Péter Török
Unterstützt der generierte Code auch die parallele Ausführung?
@ Péter Török, das bedeutet einfach, dass BLAS und LAPACK in Fortran früher eine viel bessere Leistung erbrachten als alle ihre C / C ++ - Ports. Jetzt schrumpft die Lücke schnell.
SK-logic
6
Sie können nur argumentieren, dass ein Compiler schneller Code erzeugt, wenn Sie ein zu 100% gleichwertiges Programm in beiden Sprachen haben, das von Experten geschrieben wurde, die ihre Compiler kennen und für die Leistung verantwortlich sind.
Falcon
Der frühere Fortran unterstützte keine Rekursion und musste daher die Funktionsaufrufargumente nicht unbedingt auf den Stapel schieben, da für die Argumente jeder Funktion ein statisch zugewiesener Platz vorhanden wäre. Dies ist einer der Gründe, warum es schneller hätte sein können. Eine vollständigere Antwort finden Sie vermutlich hier: amazon.com/Programming-Language-Pragmatics-Third-Edition/dp/…
Pedro Rolo

Antworten:

36

IIRC Einer der Hauptgründe, warum Fortran als schneller gilt, ist das Fehlen von Pointer-Aliasing. Daher können sie Optimierungen verwenden, die C-Compiler nicht verwenden können:

In FORTRAN dürfen sich Funktionsargumente nicht gegenseitig aliasieren, und der Compiler geht davon aus, dass dies nicht der Fall ist. Dies ermöglicht eine hervorragende Optimierung und ist einer der Hauptgründe für den Ruf von FORTRAN als schnelle Sprache. (Beachten Sie, dass Aliasing möglicherweise immer noch in einer FORTRAN-Funktion auftritt. Wenn beispielsweise A ein Array ist und i und j Indizes sind, die zufällig denselben Wert haben, dann sind A [i] und A [j] zwei unterschiedliche Namen für die gleicher Speicherort. Da das Basis-Array den gleichen Namen haben muss, kann zum Glück eine Indexanalyse durchgeführt werden, um Fälle zu bestimmen, in denen A [i] und A [j] keinen Alias ​​haben können.)

Aber ich stimme anderen hier zu: Der Vergleich der durchschnittlichen Anzahl von Assembler-Anweisungen, die für eine Codezeile generiert wurden, ist völliger Unsinn. Beispielsweise kann ein moderner x86-Kern zwei Befehle parallel ausführen, wenn er nicht auf dieselben Register zugreift. Sie können also (theoretisch) eine Leistungssteigerung von 100% für denselben Befehlssatz erzielen, indem Sie ihn einfach neu anordnen . Gute Compiler generieren häufig auch mehr Assembler-Anweisungen, um schnelleren Code zu erhalten (think loop unrolling, inlining). Die Gesamtzahl der Assembler-Anweisungen sagt wenig über die Leistung eines Codeteils aus.

Nikie
quelle
Ein weiterer Grund für bessere Optimierungen ist die native Unterstützung für komplexe Zahlen.
SK-logic
Sicher richtig für Fortran IV oder so. Nicht sicher, ob moderne FORTRANs noch keine Zeiger, dynamische Speicher usw. haben
Ingo
2
Aus dem gleichen Grund haben wir uns bei der Entwicklung in C und C ++ in der Spielebranche häufig auf Inline-Assemblierung beschränkt. Menschen können so oft sie wollen behaupten, dass "Compiler besser optimieren können als Menschen, die Assembler schreiben". Tatsache ist, dass Zeiger-Aliasing bedeutet, dass sie dies oft nicht können . Der Code, den wir von Hand schreiben können, ist technisch gesehen für den Compiler unzulässig, da er nichts mit Pointer-Aliasing zu tun hat.
Carson63000
5
Mit dem restrictSchlüsselwort von C kann der Autor einer Funktion angeben, dass ein Zeiger keine Aliase enthält. Reicht dies aus, um den Unterschied zu beheben, oder steckt mehr dahinter?
bk.
@bk .: Cs "einschränken" greift "das halbe Problem" an; Man kann damit sagen, dass ein bestimmter Zeiger innerhalb seiner Lebensdauer keinen Alias ​​mehr hat, aber es gibt keine Möglichkeit, einem Compiler mitzuteilen, dass ein Objekt, dessen Adresse an eine Funktion übergeben wurde, nach der Rückkehr dieser Funktion keinen Alias ​​mehr hat.
Supercat
8

Völlig ungültiger Vergleich.

Zunächst müssen Sie, wie @ Péter Török betont, die Anzahl der Zeilen in entsprechenden Programmen von Fortran und C vergleichen um überhaupt einen gültigen Vergleich der Anzahl der produzierten Zeilen zu erhalten.

Zweitens entsprechen weniger Codezeilen nicht immer schnelleren Programmen . Nicht alle Maschinenbefehle erfordern die gleiche Anzahl von Zyklen , aber Sie haben auch andere Probleme wie Speicherzugriff , Zwischenspeicherung usw.

Darüber hinaus können lange Code-Läufe schneller sein, da dies zu einer geringeren Anzahl von Ausführungszeilen führt (dh Line Count! = Executed Line Count ).

Dan McGrath
quelle
5

Dan ist richtig, längere Programme bedeuten nicht langsamere Programme. Es hängt stark davon ab, was sie tun.

Ich bin kein Experte für Fortran, ich weiß ein bisschen. Wenn ich sie vergleiche, würde gut geschriebenes C mit komplexeren Datenstrukturen und Funktionen eine viel bessere Leistung bringen als Fortran. Jemand (bitte) korrigiert mich, wenn ich mich hier irre, aber ich denke, Fortran ist ein bisschen "niedriger" als C. Wenn ja, würde Fortran mit Sicherheit einige Probleme schneller lösen.

Eine andere Sache, auf den ersten Blick dachte ich, Sie fragten, ob die Compiler schneller sind. Ich denke tatsächlich, dass Fortran im Allgemeinen schneller für ähnliche Codemengen kompiliert wird, aber das resultierende Programm und die Art und Weise, wie es ausgeführt wird, wären eine andere Geschichte. Es ist nur einfacher zu analysieren.

Garet Claborn
quelle
2
Wenn Sie komplexe Datenstrukturen verwenden, ist FORTRAN wahrscheinlich die falsche Wahl. FORTRAN ist für die schnelle Eingabe einfacher Zahlen optimiert.
Zachary K
4

Ich denke, ein Teil davon ist, dass FORTRAN-Compiler entworfen wurden, um einige Arten von Mathematik sehr schnell durchzuführen. Aus diesem Grund wird FORTRAN verwendet, um Berechnungen so schnell wie möglich durchzuführen

Zachary K
quelle
4

Die Aussage mag in früheren Zeiten (ca. Ende der 70er Jahre) zutreffen, als C noch in den Kinderschuhen steckte, und Fortran wurde von allen großen Herstellern unterstützt und stark optimiert. Frühe Fortrans basierten auf der IBM-Architektur, also einfache Sachen wie die Arithmetik, wenn sicherlich eine Aussage pro Montageanweisung gewesen wäre. Dies gilt für ältere Maschinen wie Data General und Prime, die 3-Wege-Sprünge hatten. Dies funktioniert bei modernen Befehlssätzen ohne 3-Wege-Sprung nicht.

Codezeilen sind nicht gleich Code-Anweisungen. In früheren Versionen von Fortran war nur eine Anweisung pro Zeile zulässig. In späteren Versionen von Fortran können mehrere Anweisungen pro Zeile verwendet werden. C kann mehrere Anweisungen pro Zeile enthalten. Auf den schnelleren Produktionscompilern wie Intels IVF (ehemals CVF, MS Powerstation) und Intels C gibt es keinen Unterschied zwischen den beiden. Diese Compiler sind stark optimiert.

Tasse
quelle
4

Bei FORTRAN alter Art musste ein Programmierer, der einen Teil eines Arrays für eine Funktion verfügbar machen wollte, einen Verweis auf das gesamte Array zusammen mit einem oder mehreren ganzzahligen Werten übergeben, die den Startindex und entweder den Endindex oder die Anzahl der Elemente angeben . C macht es möglich, dies zu vereinfachen, indem ein Zeiger zusammen mit der Anzahl der Elemente an den Anfang des interessierenden Abschnitts übergeben wird . Direkt ausgedrückt würde dies die Dinge schneller machen (zwei Dinge anstatt drei). Indirekt kann es jedoch zu einer Verlangsamung kommen, indem die Optimierungsarten, die ein Compiler ausführen kann, eingeschränkt werden.

Betrachten Sie die Funktion:

void diff(float dest[], float src1[], float src2[], int n)
{
  for (int i=0; i<n; i++)
    dest[i] = src1[i] - src2[i];
}

Wenn ein Compiler wüsste, dass jeder der Zeiger den Anfang eines Arrays identifiziert, könnte er Code generieren, der auf Elemente des Arrays parallel oder in beliebiger Reihenfolge einwirkt, da für x! = y Operationen auf dest [x ] beeinflusst weder src1 [y] noch src2 [y]. Zum Beispiel kann ein Compiler auf einigen Systemen davon profitieren, Code zu generieren, der äquivalent ist zu:

void dif(float dest[], float src1[], float src2[], int n)
{
  int i=0;
  float t1a,t1b,t2a,t2b,tsa,tsb;
  if (n > 2)
  {
    n-=4;
    t1a = src1[n+3]; t1b = src2[n+3]; t1b=src2[n+2]; t2b = src2[n+2];
    do
    {
      tsa = t1a-t2a;
      t1a = src1[n+1]; t2a = src2[n+1]; 
      tsb = t2b-t2b;
      dest[n+3] = tsa;
      t1b = src1[n]; t2b = src2[n]; 
      n-=2;
      dest[n+4] = tsb;
    } while(n >= 0);
    ... add some extra code to handle cleanup
  }
  else
    ... add some extra code to handle small values of n
}

Beachten Sie, dass jede Operation, die einen Wert lädt oder berechnet, mindestens eine weitere Operation zwischen sich und der nächsten Operation hat, die diesen Wert verwendet. Einige Prozessoren können die Verarbeitung verschiedener Vorgänge überlappen, wenn diese Bedingungen erfüllt sind, wodurch die Leistung verbessert wird. Beachten Sie jedoch, dass ein C-Compiler die obige Transformation nicht ausführen kann, da ein C-Compiler nicht weiß, dass der Code keine Zeiger auf teilweise überlappende Bereiche eines gemeinsamen Arrays übergeben wird. FORTRAN-Compiler, denen äquivalenter Code zugewiesen wurde, konnten und haben jedoch eine solche Transformation durchgeführt.

Während ein C-Programmierer versuchen könnte, eine vergleichbare Leistung zu erzielen, indem er explizit Code schreibt, der die Schleife abwickelt und die Operationen benachbarter Durchläufe überlappt, könnte ein solcher Code die Leistung leicht verschlechtern, wenn er so viele automatische Variablen verwendet, dass ein Compiler sie "verschütten" muss Erinnerung. Der Optimierer eines FORTRAN-Compilers weiß wahrscheinlich mehr als ein Programmierer darüber, welche Formen der Verschachtelung in einem bestimmten Szenario eine optimale Leistung erbringen würden, und solche Entscheidungen sollten häufig solchen Compilern überlassen werden. Während C99 versuchte, die Situation von C durch Hinzufügen eines restrictQualifizierers zu verbessern , konnte dies hier nur verwendet werden, wenn dest[]es sich um ein von beiden src1[]und getrenntes Array handelte src2[], oder wenn der Programmierer separate Versionen der Schleife hinzufügte, um die Fälle zu behandeln, in denen alle nicht destzusammenhängend warensrc1undsrc2 , wosrc1[]und destwaren gleich und src2waren unzusammenhängend, wo src2[]und dest[]waren gleich und src1waren unzusammenhängend, und wo alle drei Reihen gleich waren. Im Gegensatz dazu konnte FORTRAN alle vier Fälle problemlos mit demselben Quellcode und demselben Maschinencode behandeln.

Superkatze
quelle