I compilatori Fortran generano davvero un codice più veloce dei compilatori C?


17

Quando studiavo all'università sentivo spesso l'idea che i compilatori Fortran producessero codice più veloce dei compilatori C per un programma equivalente.

Il ragionamento chiave è andato così: un compilatore Fortran emette in media 1,1 istruzioni per processore per riga di codice, mentre un compilatore C emette in media 1,6 istruzioni per processore per riga di codice - non ricordo i numeri esatti ma il l'idea era che i compilatori C emettessero notevolmente più codice macchina e quindi producessero programmi più lenti.

Quanto è valido tale confronto? Possiamo dire che i compilatori Fortran producono programmi più veloci dei compilatori C o viceversa e perché esiste questa differenza?


19
Ciò può significare semplicemente che i programmi Fortran sono più dettagliati di C ... Un confronto significativo potrebbe essere fatto solo implementando la stessa funzionalità in entrambe le lingue e confrontando il codice macchina risultante (dimensioni e velocità).
Péter Török,

Inoltre, il codice generato supporta l'esecuzione parallela?

@Péter Török, significa semplicemente che, per esempio, BLAS e LAPACK in Fortran avevano prestazioni molto migliori rispetto a qualsiasi delle loro porte C / C ++. Ora il divario si sta riducendo rapidamente.
SK-logic

6
Puoi solo sostenere che un compilatore produce codice più veloce se hai un programma equivalente al 100% in entrambe le lingue, scritto da esperti che conoscono i loro compilatori e che possono rendere conto delle prestazioni.
Falcon,

Il precedente Fortran non supportava la ricorsione e quindi non doveva necessariamente spingere gli argomenti della chiamata di funzione nello stack poiché ci sarebbe stato uno spazio assegnato staticamente per gli argomenti di ciascuna funzione. Questo è uno dei motivi per cui avrebbe potuto essere più veloce. Immagino che potresti trovare una risposta più completa qui: amazon.com/Programming-Language-Pragmatics-Third-Edition/dp/…
Pedro Rolo,

Risposte:


36

IIRC uno dei motivi principali per cui si dice che Fortran è più veloce è l'assenza di alias dei puntatori , quindi possono usare ottimizzazioni che i compilatori C non possono usare:

In FORTRAN, gli argomenti di funzione potrebbero non aliasarsi l'un l'altro e il compilatore presuppone che non lo facciano. Ciò consente un'ottimizzazione eccellente ed è una delle ragioni principali della reputazione di FORTRAN come linguaggio veloce. (Si noti che l'aliasing può ancora verificarsi all'interno di una funzione FORTRAN. Ad esempio, se A è un array e i e j sono indici che hanno lo stesso valore, A [i] e A [j] sono due nomi diversi per stessa posizione di memoria. Fortunatamente, poiché l'array di base deve avere lo stesso nome, l'analisi dell'indice può essere eseguita per determinare i casi in cui A [i] e A [j] non possono alias.)

Ma sono d'accordo con gli altri qui: confrontare il numero medio di istruzioni assembler generate per una riga di codice è un'assurdità completa. Ad esempio un moderno core x86 può eseguire due istruzioni in parallelo se non accedono agli stessi registri. Quindi puoi (in teoria) ottenere un aumento delle prestazioni del 100% per lo stesso set di istruzioni semplicemente riordinandole . Buoni compilatori generano spesso anche più istruzioni di assemblaggio per ottenere un codice più veloce (think loop svolgendo, inline). Il numero totale di istruzioni dell'assemblatore dice molto poco sulle prestazioni di un pezzo di codice.


Un altro motivo per una migliore ottimizzazione è il supporto nativo per numeri complessi.
Logica SK

Certamente corretto per Fortran IV o giù di lì. Non sono sicuro che i moderni FORTRAN non abbiano ancora puntatori, memoria dinamica ecc.
Ingo

2
Questo è lo stesso motivo per cui siamo spesso passati a un po 'di assemblaggio in linea durante lo sviluppo in C e C ++ nel settore dei giochi. Le persone possono affermare tutte le volte che vogliono che "i compilatori possano ottimizzare meglio degli umani che scrivono assemblaggi", il fatto è che alias puntatore significa che spesso non possono . Il codice che possiamo scrivere a mano sarebbe tecnicamente illegale per l'emissione del compilatore, sapendo che non fa nulla sull'alias puntatore.
Carson63000,

5
La restrictparola chiave C consente all'autore di una funzione di specificare che un puntatore non ha alias. È sufficiente per affrontare la differenza o c'è di più?
bk.

@bk .: "restringe" gli attacchi di C "metà del problema"; consente di dire che un puntatore specifico non avrà alias nient'altro durante la sua vita, ma non c'è modo di dire a un compilatore che un oggetto il cui indirizzo è stato passato a una funzione non sarà aliasato da nulla una volta che la funzione ritorna.
supercat,

8

Confronto completamente non valido.

In primo luogo, come sottolinea @ Péter Török, è necessario innanzitutto confrontare il numero di righe in programmi equivalenti di Fortran e C affinché questo sia anche un valido confronto sul numero di righe prodotte.

In secondo luogo, meno righe di codice non equivalgono sempre a programmi più veloci . Non tutte le istruzioni della macchina richiedono lo stesso numero di cicli per l'esecuzione , ma hai anche altri problemi come l' accesso alla memoria , la memorizzazione nella cache , ecc.

Inoltre, l'esecuzione di codici lunghi può essere più veloce poiché comporta un numero inferiore di righe di esecuzione (ovvero Conteggio righe! = Conteggio righe eseguite ).


5

Dan ha ragione, i programmi più lunghi non significano programmi più lenti. Dipende molto da quello che stanno facendo.

Non sono un esperto di Fortran, lo so un po '. Confrontandoli, penso che C ben scritto farebbe molto meglio in termini di prestazioni con strutture di dati e funzionalità più complesse di Fortran. Qualcuno (per favore) mi corregga se sbaglio qui, ma penso che Fortran sia un po 'a un livello più basso di C. In tal caso, sono sicuro che alcuni problemi potrebbero emergere più velocemente su Fortran.

Un'altra cosa, a prima vista, ho pensato che stavi chiedendo se i compilatori sono più veloci. In realtà penso che Fortran generalmente si compili più velocemente per quantità simili di codice, ma il programma risultante e come funziona sarebbe una storia diversa. È solo più semplice analizzare.


2
Se stai utilizzando strutture di dati complesse FORTRAN è probabilmente la scelta sbagliata. FORTRAN è ottimizzato per eseguire scricchiolii di numeri molto velocemente.
Zaccaria K,

4

Penso che parte di ciò sia che i compilatori FORTRAN sono progettati per eseguire alcuni tipi di matematica molto velocemente. Questo è il motivo per cui le persone usano FORTRAN, per fare calcoli il più velocemente possibile


4

L'affermazione potrebbe essere stata vera ai vecchi tempi (verso la fine degli anni '70) quando C era agli inizi, e Fortran era supportato da tutti i principali produttori ed era altamente ottimizzato. I primi Fortrans erano basati sull'architettura IBM, cose così semplici come l'aritmetica se certamente fosse stata un'istruzione per istruzione di assemblaggio. Questo è vero per le macchine più vecchie come Data General e Prime, che avevano salti a 3 vie. Questo non funziona con i set di istruzioni moderni che non hanno un salto a 3 vie.

Le righe di codice non equivalgono a istruzioni di codice. Le versioni precedenti di Fortran consentivano solo un'istruzione per riga. Le versioni successive di Fortran possono richiedere più istruzioni per riga. C può avere più istruzioni per riga. Sui compilatori di produzione più veloci come Intel IVF (ex CVF, MS Powerstation) e Intel C, non c'è davvero alcuna differenza tra i due. Questi compilatori sono altamente ottimizzati.


4

Il FORTRAN vecchio stile richiedeva che un programmatore che desiderasse rendere disponibile una parte di un array per una funzione necessaria per passare un riferimento all'intero array, insieme a uno o più valori interi che specificano il pedice iniziale e il pedice finale o il numero di elementi . C rende possibile semplificare questo passaggio di un puntatore all'inizio della porzione di interesse insieme al numero di elementi. In termini diretti, ciò renderebbe le cose più veloci (passando due cose anziché tre). Indirettamente, tuttavia, potrebbe finire per rallentare le cose limitando i tipi di ottimizzazione che un compilatore può eseguire.

Considera la funzione:

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

se un compilatore sapesse che ciascuno dei puntatori identificherebbe l'inizio di un array, potrebbe generare codice che agirebbe su elementi dell'array in parallelo o in qualsiasi ordine, poiché per qualsiasi x! = y, operazioni su dest [x ] non influenzerà src1 [y] né src2 [y]. Ad esempio, su alcuni sistemi un compilatore può trarre vantaggio dalla generazione di codice equivalente a:

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
}

Si noti che ogni operazione che carica o calcola un valore ha almeno un'altra operazione tra essa e l'operazione successiva che utilizza quel valore. Alcuni processori possono sovrapporsi al trattamento di diverse operazioni quando tali condizioni sono soddisfatte, migliorando così le prestazioni. Si noti, tuttavia, che poiché un compilatore C non ha modo di sapere che il codice non passerà i puntatori alle aree parzialmente sovrapposte di un array comune, un compilatore C non può effettuare la trasformazione sopra. I compilatori FORTRAN con un codice equivalente, tuttavia, potevano e hanno fatto una tale trasformazione.

Mentre un programmatore C potrebbe tentare di ottenere prestazioni comparabili scrivendo esplicitamente il codice che ha srotolato il loop e si è sovrapposto alle operazioni dei passaggi adiacenti, tale codice potrebbe facilmente degradare le prestazioni se usasse così tante variabili automatiche che un compilatore ha dovuto "riversarle" in memoria. L'ottimizzatore di un compilatore FORTRAN probabilmente saprebbe più di un programmatore su quali forme di interfogliatura produrrebbero prestazioni ottimali in un dato scenario, e tali decisioni sono spesso meglio lasciare a tali compilatori. Mentre C99 ha tentato di migliorare in qualche modo la situazione di C aggiungendo un restrictqualificatore, questo può essere usato qui solo se dest[]era un array separato da entrambi src1[]e src2[], o se il programmatore ha aggiunto versioni separate del ciclo per gestire i casi in cui tutto destera disgiuntosrc1e src2, dove src1[]ed desterano uguali ed src2era disgiunto, dove src2[]ed dest[]erano uguali ed src1era disgiunto, e dove tutte e tre le matrici erano uguali. FORTRAN, al contrario, potrebbe gestire tutti e quattro i casi senza difficoltà usando lo stesso codice sorgente e lo stesso codice macchina.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.