In che modo un linguaggio il cui compilatore è scritto in C può mai essere più veloce di C?


175

Dando un'occhiata alla pagina web di Julia , puoi vedere alcuni benchmark di diverse lingue attraverso diversi algoritmi (i tempi mostrati di seguito). Come può una lingua con un compilatore originariamente scritto in C, superare il codice C?

inserisci qui la descrizione dell'immagine Figura: tempi di riferimento relativi a C (minore è meglio, prestazione C = 1,0).



382
Come può un'auto, che è un oggetto creato dall'uomo, muoversi più velocemente di un uomo?
babou,

19
Secondo la tabella, Python è più lento di C. Pensi che sia impossibile scrivere un compilatore C in Python che generi lo stesso codice del tuo compilatore C preferito? E in che lingua è scritto comunque?
Carsten S,

6
Il commento di Babou è stato perfetto, ma non credo che abbiamo bisogno di più versioni dello stesso.
Raffaello

14
Un pensiero correlato. Molti compilatori sono self hosting , nel senso che sono scritti nella loro lingua (spesso più un po 'di assembly) e compilati con la versione precedente di se stesso. Eppure i compilatori migliorano e diventano più veloci. Colpo di mente .
Schwern,

Risposte:


263

Non esiste alcuna relazione necessaria tra l'implementazione del compilatore e l'output del compilatore. Potresti scrivere un compilatore in un linguaggio come Python o Ruby, le cui implementazioni più comuni sono molto lente, e quel compilatore potrebbe generare un codice macchina altamente ottimizzato in grado di superare le prestazioni di C. Il compilatore stesso richiederebbe molto tempo per essere eseguito, perché il suoil codice è scritto in un linguaggio lento. (Per essere più precisi, scritto in una lingua con un'implementazione lenta. Le lingue non sono intrinsecamente veloci o lente, come sottolinea Raphael in un commento. Espando questa idea di seguito.) Il programma compilato sarebbe veloce quanto il suo è consentita l'implementazione propria: potremmo scrivere un compilatore in Python che genera lo stesso codice macchina di un compilatore Fortran, ei nostri programmi compilati sarebbero veloci come Fortran, anche se richiederebbero molto tempo per la compilazione.

È una storia diversa se stiamo parlando di un interprete. Gli interpreti devono essere in esecuzione mentre il programma che stanno interpretando è in esecuzione, quindi esiste una connessione tra la lingua in cui è implementato l'interprete e l'esecuzione del codice interpretato. È necessaria un'ottimizzazione runtime intelligente per creare un linguaggio interpretato che corre più veloce della lingua in cui è implementato l'interprete e le prestazioni finali possono dipendere da quanto un pezzo di codice sia suscettibile a questo tipo di ottimizzazione. Molti linguaggi, come Java e C #, utilizzano i runtime con un modello ibrido che combina alcuni dei vantaggi degli interpreti con alcuni dei vantaggi dei compilatori.

Come esempio concreto, esaminiamo più da vicino Python. Python ha diverse implementazioni. Il più comune è CPython, un interprete di bytecode scritto in C. C'è anche PyPy, che è scritto in un dialetto specializzato di Python chiamato RPython e che utilizza un modello di compilazione ibrido in qualche modo simile alla JVM. PyPy è molto più veloce di CPython nella maggior parte dei benchmark; utilizza tutti i tipi di trucchi sorprendenti per ottimizzare il codice in fase di esecuzione. Tuttavia, il linguaggio Python eseguito da PyPy è esattamente lo stesso linguaggio Python utilizzato da CPython, salvo alcune differenze che non influiscono sulle prestazioni.

Supponiamo di aver scritto un compilatore in linguaggio Python per Fortran. Il nostro compilatore produce lo stesso codice macchina di GFortran. Ora compiliamo un programma Fortran. Possiamo eseguire il nostro compilatore su CPython o eseguirlo su PyPy, poiché è scritto in Python ed entrambe queste implementazioni eseguono lo stesso linguaggio Python. Quello che troveremo è che se eseguiamo il nostro compilatore su CPython, quindi eseguiamo su PyPy, quindi compiliamo lo stesso sorgente Fortran con GFortran, avremo esattamente lo stesso codice macchina tutte e tre le volte, quindi il programma compilato verrà sempre eseguito alla stessa velocità. Tuttavia, il tempo necessario per produrre quel programma compilato sarà diverso. Molto probabilmente CPython impiegherà più tempo di PyPy e PyPy richiederà molto più tempo di GFortran, anche se alla fine tutti genereranno lo stesso codice macchina.

Dalla scansione della tabella di benchmark del sito Web Julia, sembra che nessuna delle lingue in esecuzione sugli interpreti (Python, R, Matlab / Octave, Javascript) abbia benchmark in cui battono C. Questo è generalmente coerente con ciò che mi aspetterei di vedere, sebbene potessi immaginare il codice scritto con la libreria Numpy altamente ottimizzata di Python (scritta in C e Fortran) che batte alcune possibili implementazioni in C di codice simile. I linguaggi uguali o migliori di C vengono compilati (Fortran, Julia ) o usando un modello ibrido con compilazione parziale (Java e probabilmente LuaJIT). PyPy utilizza anche un modello ibrido, quindi è del tutto possibile che se avessimo eseguito lo stesso codice Python su PyPy anziché su CPython, lo vedremmo effettivamente battere C su alcuni benchmark.


9
Questa è una risposta straordinaria. Molto chiaro, comprensibile e informativo. Grazie mille per aver dedicato del tempo a scriverlo!
Alex A.

7
Sia javascript che java vengono eseguiti con un compilatore JIT, ma java ha un test in cui è più veloce di C. Il motivo principale per cui un runtime / compilatore può essere eseguito più velocemente è dovuto alla disponibilità di maggiori informazioni. I compilatori C / C ++ possono ottimizzare il codice (di solito) molto più di qualcuno che scrive assembly a mano, semplicemente perché il compilatore ha più informazioni disponibili. Certo, in teoria la persona potrebbe scrivere un codice assembly migliore, ma ciò richiede più conoscenze e abilità di quante ne abbiano la maggior parte delle persone. Le lingue JIT possono espandersi ancora di più su questo, essendo in grado di ottimizzare esattamente la macchina su cui è in esecuzione
Programmdude,

Quali ottimizzazioni fa il compilatore è una cosa importante da considerare. Un compilatore davvero intelligente riconoscerebbe che il programma è un benchmark sintetico e semplicemente ottimizzerebbe via quasi tutto il codice, creando semplicemente l'output previsto.
ghellquist,

@ghellquist Certo, se il benchmark è abbastanza artificiale e il compilatore è abbastanza intelligente. Ciò non è direttamente o direttamente correlato al linguaggio di implementazione del compilatore, quindi non ne ho parlato qui.
Tsleyson,

97

Come può una macchina costruita da un uomo essere più forte di un uomo? Questa è esattamente la stessa domanda.

La risposta è che l'output del compilatore dipende dagli algoritmi implementati da quel compilatore, non dalla lingua utilizzata per implementarlo. Potresti scrivere un compilatore molto lento e inefficiente che produce codice molto efficiente. Non c'è niente di speciale in un compilatore: è solo un programma che accetta input e produce output.


33
Come può un programma di scacchi battere l'umano che lo ha scritto?
Thorbjørn Ravn Andersen,

25
Facendo mosse migliori! <rimshot>
Tony Ennis

Per parafrasare la risposta di Penn Gilette al perché non importa che un computer possa battere un uomo a scacchi: "Ti aspetteresti che un robot progettato da GE perda a un uomo in un incontro di boxe?"
Dave Kanter,

90

Voglio fare un punto contro un presupposto comune che, a mio avviso, è fallace al punto da essere dannoso quando si scelgono gli strumenti per un lavoro.

Non esiste un linguaggio lento o veloce. ¹

Sulla strada per la CPU che sta effettivamente facendo qualcosa, ci sono molti passaggi².

  1. Almeno un programmatore con determinati skillset.
  2. Il linguaggio (formale) in cui programmano ("codice sorgente").
  3. Le librerie che usano.
  4. Qualcosa che traduce il codice sorgente in codice macchina (compilatori, interpreti).
  5. L'architettura hardware complessiva, ad esempio il numero di unità di elaborazione e il layout della gerarchia di memoria.
  6. Il sistema operativo che gestisce l'hardware.
  7. Ottimizzazioni su CPU.

Ogni singolo elemento contribuisce al tempo di esecuzione effettivo che puoi misurare, a volte pesantemente. Diverse "lingue" si concentrano su cose diverse³.

Giusto per fare alcuni esempi.

  • 1 vs 2-4 : è probabile che un programmatore C medio produca un codice molto peggiore di un programmatore Java medio, sia in termini di correttezza che di efficienza. Questo perché il programmatore ha più responsabilità in C.

  • 1/4 vs 7 : in un linguaggio di basso livello come C, potresti essere in grado di sfruttare alcune funzioni della CPU come programmatore . Nei linguaggi di livello superiore, solo il compilatore / interprete può farlo e solo se conoscono la CPU di destinazione.

  • 1/4 vs 5 : vuoi o devi controllare il layout della memoria per utilizzare al meglio l'architettura di memoria a portata di mano? Alcune lingue ti danno il controllo, altre no.

  • 2/4 vs 3 : lo stesso Python interpretato è terribilmente lento, ma ci sono legami popolari con librerie altamente ottimizzate e compilate in modo nativo per il calcolo scientifico. Quindi fare certe cose in Python è veloce alla fine, se la maggior parte del lavoro è fatta da queste librerie.

  • 2 vs 4 : l'interprete standard di Ruby è piuttosto lento. JRuby, d'altra parte, può essere molto veloce. Questa è la stessa lingua è veloce usando un altro compilatore / interprete.

  • 1/2 vs 4 : utilizzando le ottimizzazioni del compilatore, il codice semplice può essere tradotto in un codice macchina molto efficiente.

La linea di fondo è che il benchmark che hai trovato non ha molto senso, almeno non quando si riduce a quella tabella che includi. Anche se tutto ciò che ti interessa è il tempo di esecuzione, devi specificare l' intera catena dal programmatore alla CPU; lo scambio di uno qualsiasi degli elementi può cambiare radicalmente i risultati.

Per essere chiari, questo risponde alla domanda perché mostra che la lingua in cui è scritto il compilatore (passaggio 4) non è che un pezzo del puzzle, e probabilmente non è affatto pertinente (vedi altre risposte).


  1. Ci sono certamente funzionalità linguistiche che sono più costose da implementare rispetto ad altre. Ma l'esistenza di funzionalità non significa che devi usarle, e una funzione costosa può salvare l'uso di molte più economiche e quindi pagare alla fine. (Di avere altri vantaggi non misurabili in tempo di esecuzione.)
  2. Salto il livello algoritmico perché non sempre si applica ed è per lo più indipendente dal linguaggio di programmazione utilizzato. Tieni presente che, ad esempio, algoritmi diversi si prestano meglio a hardware diverso.
  3. Non vado deliberatamente in diverse metriche di successo qui: efficienza del tempo di esecuzione, efficienza della memoria, tempo degli sviluppatori, sicurezza, sicurezza, (dimostrabile?) Correttezza, supporto degli strumenti, indipendenza della piattaforma, ...

    Confrontare le lingue con una metrica anche se sono state progettate per obiettivi completamente diversi è un errore enorme.


1
@babou D'accordo, spiegazione molto bella. Quindi quale sarebbe una metrica migliore, o forse un insieme di metriche , che può essere utilizzata per confrontare le lingue con i rispettivi compilatori / interpreti? Inoltre, un piccolo pignolo: dici "Non esiste un linguaggio lento o veloce" e poi "Python stesso è terribilmente lento", ma presumo tu intendessi l'interprete Python.
StrugglingProgrammer

2
@benalbrecht Il mio punto è che non esiste un unico buon insieme di tali parametri. È un compromesso, sempre. Se costruisci driver di dispositivo, vuoi essere corretto sopra ogni cosa. Se costruisci la spina dorsale di Twitter, vuoi essere efficiente sopra ogni altra cosa. In entrambi i casi, usi gli strumenti e assumi le persone che lo consentono. Se sei un utente esperto di app Android, usi ciò che la tua gente sa e / o ciò che minimizza il time-to-market. Se insegni algoritmi, vuoi una lingua con sintassi concisa, chiara e piccola piastra di comando. E così via. Le priorità differiscono e quindi abbiamo lingue diverse.
Raffaello


23

C'è una cosa dimenticata sull'ottimizzazione qui.

C'è stato un lungo dibattito sul fortran che ha sovraperformato C. Mettendo da parte il dibattito malformato: lo stesso codice è stato scritto in C e fortran (come pensavano i tester) e le prestazioni sono state testate sulla base degli stessi dati. Il problema è che queste lingue differiscono, C consente l'aliasing dei puntatori, mentre fortran no.

Quindi i codici non erano gli stessi, non c'era nessun __restrict nei file testati C, che davano differenze, dopo aver riscritto i file per dire al compilatore che può ottimizzare i puntatori, i tempi di esecuzione diventano simili.

Il punto qui è che alcune tecniche di ottimizzazione sono più facili (o iniziano ad essere legali) nella lingua appena creata.

Inoltre è possibile a lungo termine su VM con JIT sovraperformare C. Esistono due possibilità: il
codice JIT può sfruttare la macchina che lo ospita (ad esempio alcuni SSE o altri esclusivi per alcune istruzioni vettoriali CPU) che non sono stati implementati in programma comparato.X

In secondo luogo, la VM può eseguire il test della pressione durante l'esecuzione, quindi può prendere il codice pressato e ottimizzarlo o addirittura precalcolarlo durante il runtime. In anticipo il programma C compilato non prevede dove sia la pressione o (il più delle volte) ci sono versioni generiche di eseguibili per la famiglia generale di macchine.

In questo test c'è anche JS, beh ci sono macchine virtuali più veloci di V8 e in alcuni test funziona anche più velocemente di C.

L'ho verificato e c'erano tecniche di ottimizzazione uniche non ancora disponibili nei compilatori C.

Il compilatore C dovrebbe eseguire contemporaneamente l'analisi statica dell'intero codice, marciare su una determinata piattaforma e aggirare i problemi di allineamento della memoria.

La VM ha appena traslitterato parte del codice per ottimizzare l'assemblaggio ed eseguirlo.

Informazioni su Julia - come ho verificato, funziona su AST di codice, ad esempio GCC ha saltato questo passaggio e di recente ha iniziato a prendere alcune informazioni da lì. Questo oltre ad altri vincoli e tecniche VM potrebbe spiegare un po '.

Esempio: prendiamo un semplice ciclo, che prende il punto finale iniziale dalle variabili e carica parte delle variabili nei calcoli conosciuti in fase di esecuzione.

Il compilatore C genera variabili di caricamento dai registri.
Ma in fase di esecuzione queste variabili sono conosciute e trattate come costanti attraverso l'esecuzione.
Quindi, invece di caricare variabili dai registri (e non eseguire la memorizzazione nella cache perché può cambiare, e dall'analisi statica non è chiaro) vengono trattate completamente come costanti e piegate, propagate.


12

Le risposte precedenti danno praticamente la spiegazione, sebbene principalmente da un punto di vista pragmatico, per quanto la domanda abbia un senso , come spiegato in modo eccellente dalla risposta di Raffaello .

Aggiungendo a questa risposta, dovremmo notare che, al giorno d'oggi, i compilatori C sono scritti in C. Naturalmente, come notato da Raphael, il loro output e le sue prestazioni possono dipendere, tra le altre cose, dalla CPU su cui è in esecuzione. Ma dipende anche dalla quantità di ottimizzazione eseguita dal compilatore. Se scrivi in ​​C un compilatore con ottimizzazione migliore per C (che poi compili con quello precedente per poterlo eseguire), otterrai un nuovo compilatore che rende C un linguaggio più veloce di prima. Quindi, qual è la velocità di C? Nota che puoi persino compilare il nuovo compilatore con se stesso, come secondo passaggio, in modo che si compili in modo più efficiente, pur continuando a fornire lo stesso codice oggetto. E il teorema della piena occupazione mostra che il loro non ha fine a tali miglioramenti (grazie a Raffaello per l'indicatore).

Ma penso che valga la pena provare a formalizzare il problema, in quanto illustra molto bene alcuni concetti fondamentali, e in particolare la visione denotazionale contro operativa delle cose.

Che cos'è un compilatore?

Un compilatore , abbreviato in se non c'è ambiguità, è una realizzazione di una funzione calcolabile che tradurrà un testo di programma calcola una funzione , scritto in un linguaggio sorgente nel testo del programma scritto in un lingua di arrivo , che si suppone per calcolare la stessa funzione .CSTCCSTP:SP SP:T TP

Da un punto di vista semantico, cioè denotazionalmente , non importa come viene calcolata questa funzione di compilazione , ovvero quale scelta viene scelta. Potrebbe anche essere fatto da un oracolo magico. Matematicamente, la funzione è semplicemente un insieme di coppie .CSTCST{(P:S,P:T)PSSPTT}

La funzione di compilazione semantica è corretta se sia e calcolano la stessa funzione . Ma questa formalizzazione si applica anche a un compilatore errato. L'unico punto è che qualunque cosa sia implementata ottiene lo stesso risultato indipendentemente dai mezzi di implementazione. Ciò che conta semanticamente è ciò che viene fatto dal compilatore, non come (e quanto velocemente) viene fatto.CSTPSPTP

In realtà ottenere da è un problema operativo , che deve essere risolto. Questo è il motivo per cui la funzione di compilazione deve essere una funzione calcolabile. Quindi qualsiasi lingua con potere Turing, indipendentemente dalla lentezza, sarà sicuramente in grado di produrre codice efficiente come qualsiasi altra lingua, anche se potrebbe farlo in modo meno efficiente.P:TP:SCST

Affinando l'argomento, probabilmente vogliamo che il compilatore abbia una buona efficienza, in modo che la traduzione possa essere eseguita in tempi ragionevoli. Quindi le prestazioni del programma di compilazione sono importanti per gli utenti, ma non hanno alcun impatto sulla semantica. Sto dicendo performance, perché la complessità teorica di alcuni compilatori può essere molto più elevata di quanto ci si aspetterebbe.

Informazioni sul bootstrap

Ciò illustrerà la distinzione e mostrerà un'applicazione pratica.

Ora è un luogo comune implementare prima una lingua con un interprete , quindi scrivere un compilatore nella lingua stessa. Questo compilatore può essere eseguito con l'interprete per tradurre qualsiasi programma in un programma . Quindi abbiamo un compilatore in esecuzione dal linguaggio al linguaggio (macchina?) , ma è molto lento, se non altro perché gira su un interprete.I S C S TSIS S C S TCST:SS I S P : S P : T STCST:SISP:SP:TST

Ma puoi usare questa funzione di compilazione per compilare il compilatore , poiché è scritto nella lingua , e quindi ottieni un compilatore scritto in la lingua di destinazione . Se si assume, come spesso accade, che è un linguaggio che viene interpretato in modo più efficiente (macchina nativo, per esempio), allora si ottiene una versione più veloce del compilatore in esecuzione direttamente nel linguaggio . Fa esattamente lo stesso lavoro (ovvero produce gli stessi programmi target), ma lo fa in modo più efficiente. S C S TCST:SS TTTCST:TTTT


"Ciò che conta semanticamente è ciò che viene fatto, non come (e quanto velocemente) viene fatto" - va menzionato che nella pratica esistono criteri non funzionali . Esistono molti programmi target funzionalmente equivalenti, ma potremmo preferirne alcuni rispetto ad altri per qualche motivo (efficienza, dimensioni, migliore allineamento della memoria, ...). Vale a dire, la vista di un compilatore come funzione definita dall'utente è più limitata di quanto desideriamo (spesso salta anche gli effetti collaterali, ad es. I / O). Serve allo scopo esplicativo che si desidera fare, però.
Raffaello

@Raphael Per quanto riguarda il teorema della piena occupazione, me ne sono reso conto (nel mio commento su C), ma non conoscevo il nome e ho rinviato la ricerca di un riferimento. Grazie per averlo fatto. --- La semantica di cui parlo è quella del compilatore, non quella del programma target. Il programma target viene preservato sintatticamente e operativamente, non solo semanticamente. O ho frainteso la tua osservazione. Ho modificato per rendere le cose più precise nel mio testo.
babou,

@Raphael Dato che non hai cancellato il tuo commento, significa che l'ho frainteso o non ho risposto correttamente? Come mai la vista del compilatore (non del programma compilato) come funzione è troppo limitata, da un punto di vista semantico. Naturalmente, come funzione, potrebbe prendere altri argomenti oltre al solo programma compilato, come le direttive di ottimizzazione), ma questo è un dettaglio che non cambierebbe la discussione.
babou,

Penso che il mio commento rappresenti un puntatore verso "c'è di più di questo modello". Quello che scrivi non è sbagliato, ma non è tutto. Teoricamente, questo sembra ovvio: "la" funzione di compilatore non è di per sé ben definita poiché ci sono infiniti possibili programmi target, tutti semanticamente equivalenti. Quale scegliere è una parte molto importante nella progettazione di compilatori.
Raffaello

@Raphael C'è solo questo modello. Stai leggendo male il mio testo: sto dicendo "una funzione calcolabile", perché altri sono possibili. Il compilatore sta implementando una specifica funzione di compilazione "curly ", che è una funzione dalla sintassi S alla sintassi T, che definisce un insieme preciso di coppie sintattiche di programmi (lettere corsive). Si suppone che queste coppie calcolino la stessa funzione "riccia ", ma il secondo componente non può essere sostituito da un altro con la stessa proprietà. Sto intenzionalmente ignorando la semantica dei programmi per mantenere le cose comparabili nei processi di traduzione. PCP
babou,

6

Secondo il teorema dello speedup di Blum ci sono programmi che scritti ed eseguiti sulla combinazione computer / compilatore molto più veloce funzioneranno più lentamente di un programma per lo stesso sul tuo primo PC con interpretato BASIC. Semplicemente non esiste un "linguaggio più veloce". Tutto quello che puoi dire è che se scrivi lo stesso algoritmo in più lingue (implementazioni; come notato, ci sono molti compilatori C diversi in giro, e ho anche incontrato un interprete C piuttosto capace), funzionerà più velocemente o più lentamente in ogni .

Non può esserci una gerarchia "sempre più lenta". Questo è un fenomeno di cui tutti parlano fluentemente diverse lingue: ogni linguaggio di programmazione è stato progettato per un tipo specifico di applicazioni e le implementazioni più utilizzate sono state amorevolmente ottimizzate per quel tipo di programmi. Sono abbastanza sicuro che, ad esempio, un programma per scherzare con stringhe scritte in Perl probabilmente batterà lo stesso algoritmo scritto in C, mentre un programma che mastica su grandi matrici di numeri interi in C sarà più veloce di Perl.


1
"Ogni linguaggio di programmazione è stato progettato per un tipo specifico di applicazioni" In realtà, la maggior parte dei linguaggi di programmazione che le persone usano effettivamente sono linguaggi generici, al contrario di essere progettati per applicazioni specifiche. È solo che alcune lingue finiscono per essere utilizzate più in certi domini principalmente a causa di effetti sociali.
Cubico,

Immagino che dipenda da quanto ampio interpreti il ​​termine "tipo specifico di applicazione". Se è vero che la maggior parte delle lingue tradizionali non sono DSL, che certamente sono stati progettati con determinati usi in mente. C è stato progettato per implementare Unix. Java è stato progettato per lo scripting di TV interattive. Smalltalk è stato progettato per insegnare ai bambini. ECMAScript è stato progettato per gli script Web lato server e lato client. Perl è stato progettato per l'elaborazione del testo e gli script Unix. PHP è stato progettato per gli script Web sul lato server. Erlang è stato progettato per essere affidabile. Lo schema è stato progettato per esplorare il ...
Jörg W Mittag

... fondamenti di OO e modello di attore. APL è stato progettato come notazione per l'insegnamento della matematica. Julia è stata progettata per la programmazione scientifica. Tutte queste lingue ora sono ovviamente usate al di fuori del loro dominio di problemi originale, ma ci sono ancora alcune proprietà in quelle lingue che le rendono una soluzione migliore o peggiore per determinati tipi di applicazioni, anche se tutte possono essere utilizzate per creare tutti i tipi di cose.
Jörg W Mittag,

4

Torniamo alla riga originale: "Come può un linguaggio il cui compilatore è scritto in C mai più veloce di C?" Penso che questo significhi davvero dire: come può un programma scritto in Julia, il cui nucleo è scritto in C, mai più veloce di un programma scritto in C? In particolare, come potrebbe essere eseguito il programma "mandel" come scritto in Julia nell'87% dei tempi di esecuzione del programma equivalente "mandel" scritto in C?

Il trattato di Babou è finora l'unica risposta corretta a questa domanda. Tutte le altre risposte finora rispondono più o meno ad altre domande. Il problema con il testo di Babou è che la descrizione teorica di "What is a compilator", lunga molti paragrafi, è scritta in termini che probabilmente il poster originale avrà difficoltà a comprendere. Chiunque afferri i concetti a cui fanno riferimento le parole "semantico", "denotazionalmente", "realizzazione", "calcolabile" e così via conoscerà già la risposta alla domanda.

La risposta più semplice è che né il codice C, né il codice Julia, sono direttamente eseguibili dalla macchina. Entrambi devono essere tradotti e questo processo di traduzione introduce molti modi in cui il codice macchina eseguibile può essere più lento o più veloce, ma produce comunque lo stesso risultato finale. Sia C che Julia fanno compilation, il che significa una serie di traduzioni in un'altra forma. Comunemente, un file di testo leggibile dall'uomo viene tradotto in una rappresentazione interna e quindi scritto come una sequenza di istruzioni che il computer può comprendere direttamente. Con alcune lingue, c'è di più, e Julia è una di queste - ha un compilatore "JIT", il che significa che l'intero processo di traduzione non deve avvenire tutto in una volta per l'intero programma. Ma il risultato finale per qualsiasi lingua è il codice macchina che non necessita di ulteriori traduzioni, codice che può essere inviato direttamente alla CPU per farlo fare qualcosa. Alla fine, QUESTO è il "calcolo", e c'è più di un modo per dire a una CPU come ottenere la risposta desiderata.

Si potrebbe immaginare un linguaggio di programmazione che ha sia un "più" che un "moltiplicatore" operatore e un altro linguaggio che ha solo "più". Se il tuo calcolo richiede moltiplicazione, una lingua sarà "più lenta" perché ovviamente la CPU può fare entrambe le cose direttamente, ma se non hai modo di esprimere la necessità di moltiplicare 5 * 5, ti resta da scrivere "5 + 5 + 5 + 5 + 5 ". Quest'ultimo impiegherà più tempo per arrivare alla stessa risposta. Presumibilmente, c'è qualcosa di simile in corso con Julia; forse la lingua consente al programmatore di dichiarare l'obiettivo desiderato di calcolare un set di Mandelbrot in un modo che non è possibile esprimere direttamente in C.

Il processore utilizzato per il benchmark è stato elencato come CPU Xeon E7-8850 a 2,00 GHz. Il benchmark C ha utilizzato il compilatore gcc 4.8.2 per produrre istruzioni per quella CPU, mentre Julia utilizza il framework del compilatore LLVM. È possibile che il backend di gcc (la parte che produce il codice macchina per una particolare architettura della CPU) non sia avanzato in qualche modo come il backend LLVM. Ciò potrebbe fare la differenza in termini di prestazioni. Ci sono anche molte altre cose in corso: il compilatore può "ottimizzare" forse impartendo istruzioni in un ordine diverso da quello specificato dal programmatore, o addirittura non facendo affatto alcune cose se è in grado di analizzare il codice e determinare che non lo sono richiesto per ottenere la risposta giusta. E il programmatore potrebbe aver scritto parte del programma C in un modo che lo rallenta, ma non

Tutti questi sono modi di dire: ci sono molti modi per scrivere il codice macchina per calcolare un set di Mandelbrot e la lingua che usi ha un effetto importante su come quel codice macchina viene scritto. Quanto più comprendi su compilation, set di istruzioni, cache e così via, tanto meglio sarai in grado di ottenere i risultati che desideri. Il principale risultato dai risultati di riferimento citati per Julia è che nessuna lingua o strumento è il migliore in tutto. In effetti il ​​miglior fattore di velocità nell'intero grafico era per Java!


2

La velocità di un programma compilato dipende da due cose:

  1. Le caratteristiche prestazionali della macchina che la esegue
  2. Il contenuto del file eseguibile

La lingua in cui è scritto un compilatore è irrilevante per (1). Ad esempio, un compilatore Java può essere scritto in C o Java o Python, ma in tutti i casi la "macchina" che esegue il programma è la JVM.

La lingua in cui è scritto un compilatore è irrilevante per (2). Ad esempio, non vi è alcun motivo per cui un compilatore C scritto in Python non possa generare esattamente lo stesso file eseguibile di un compilatore C scritto in C o Java.


1

Proverò a offrire una risposta più breve.

Il nocciolo della domanda sta nella definizione di "velocità" di una lingua .

La maggior parte se non tutti i test di confronto della velocità non verificano quale sia la velocità massima possibile. Invece scrivono un piccolo programma in una lingua che vogliono testare, per risolvere un problema. Durante la stesura del programma, il programmatore utilizza ciò che assume * come best practice e convenzioni della lingua, al momento del test. Quindi misurano la velocità con cui è stato eseguito il programma.

* Le ipotesi sono occasionalmente sbagliate.


0

Il codice scritto in un linguaggio X il cui compilatore è scritto in C, può sovraperformare un codice scritto in C, a condizione che il compilatore C abbia una scarsa ottimizzazione rispetto a quello del linguaggio X. Se teniamo fuori discussione l'ottimizzazione, allora se il compilatore di X potrebbe generare meglio codice oggetto rispetto a quello generato dal compilatore C, quindi anche il codice scritto in X può vincere la gara.

Ma se il linguaggio X è un linguaggio interpretato e l'interprete è scritto in C, e se assumiamo che l'interprete del linguaggio X e del codice scritto in C sia compilato dallo stesso compilatore C, in nessun modo il codice scritto in X andrà a sovraperformare il codice scritto in C, purché entrambe le implementazioni seguano lo stesso algoritmo e utilizzino strutture di dati equivalenti.


2
Cosa aggiunge questo alle risposte precedenti? Inoltre, non credo che il tuo secondo paragrafo sia vero, per i motivi esposti in altre risposte.
Raffaello
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.