Cosa determina la "velocità" di un linguaggio di programmazione?


38

Supponiamo che un programma sia stato scritto in due lingue distinte, lascia che siano la lingua X e la lingua Y, se i loro compilatori generano lo stesso codice byte, perché dovrei usare la lingua X invece della lingua Y? Cosa definisce che una lingua è più veloce di un'altra?

Lo chiedo perché spesso vedi persone dire cose come: "C è il linguaggio più veloce, ATS è un linguaggio veloce come C". Stavo cercando di capire la definizione di "veloce" per i linguaggi di programmazione.


21
Se un programma è più veloce di un altro, significa che non possono avere lo stesso codice byte.
svick,

5
Le lingue sono solo una nozione che si usa per scrivere programmi, quindi non si può davvero parlare della velocità di una lingua.
Juho,

1
@Raphael Sento che è fuori tema, poco chiaro e troppo vasto. Mentre l'argomento è più adatto all'ingegneria del software , sospetto che sarebbe chiuso come "troppo ampio" lì.
David Richerby,

2
A parte l'implementazione, la "velocità" è ambigua, ci sono diverse velocità per l'implementazione, la compilazione, l'esecuzione e il debug, e in genere ne scambierai alcuni per gli altri (altrimenti useremmo tutti il linguaggio di programmazione)
Nick T

Come sopra. Le lingue non generano lo stesso codice byte. Alcune lingue sono più facili da analizzare nel codice byte. Alcuni hanno un livello più alto di astrazione.
superluminario

Risposte:


23

Ci sono molte ragioni che possono essere prese in considerazione per scegliere una lingua X piuttosto che una lingua Y. La leggibilità del programma, la facilità di programmazione, la portabilità su molte piattaforme, l'esistenza di buoni ambienti di programmazione possono essere tali ragioni. Tuttavia, prenderò in considerazione solo la velocità di esecuzione richiesta nella domanda. La domanda non sembra considerare, ad esempio, la velocità di sviluppo.

Due lingue possono essere compilate nello stesso bytecode, ma ciò non significa che verrà prodotto lo stesso codice,

In realtà bytecode è solo il codice per una specifica macchina virtuale. Presenta vantaggi ingegneristici, ma non introduce differenze fondamentali con la compilazione diretta per un harware specifico. Quindi potresti anche considerare di confrontare due lingue compilate per l'esecuzione diretta sulla stessa macchina.

Detto questo, il problema della velocità relativa delle lingue è vecchio e risale ai primi compilatori.

Per molti anni, in quei primi tempi, il professionista ha ritenuto che il codice scritto a mano fosse più veloce del codice compilato. In altre parole, il linguaggio macchina era considerato più veloce di linguaggi di alto livello come Cobol o Fortran. Ed era, sia più veloce che solitamente più piccolo. Le lingue di alto livello si sviluppavano ancora perché erano molto più facili da usare per molte persone che non erano scienziati informatici. Il costo dell'utilizzo di linguaggi di alto livello aveva persino un nome: il rapporto di espansione, che poteva riguardare la dimensione del codice generato (un problema molto importante in quei tempi) o il numero di istruzioni effettivamente eseguite. Il concetto era principalmente sperimentale, ma all'inizio il rapporto era maggiore di 1, poiché i compilatori facevano un lavoro abbastanza semplice secondo gli standard odierni.

Quindi il linguaggio macchina era più veloce di quanto dicesse, Fortran.

Naturalmente, questo è cambiato nel corso degli anni, man mano che i compilatori sono diventati più sofisticati, al punto che la programmazione in linguaggio assembly è ora molto rara. Per la maggior parte delle applicazioni, i programmi di linguaggio assembly non competono bene con il codice generato dall'ottimizzazione dei compilatori.

Ciò dimostra che uno dei problemi principali è la qualità dei compilatori disponibili per la lingua considerata, la loro capacità di analizzare il codice sorgente e di ottimizzarlo di conseguenza.

Questa capacità può dipendere in parte dalle caratteristiche del linguaggio per enfatizzare le proprietà strutturali e matematiche della fonte al fine di facilitare il lavoro del compilatore. Ad esempio, un linguaggio potrebbe consentire l'inclusione di dichiarazioni sulle proprietà algebriche delle funzioni definite dall'utente, in modo da consentire al compilatore di utilizzare queste proprietà a fini di ottimizzazione.

Il processo di compilazione può essere più semplice, producendo quindi un codice migliore, quando il paradigma di programmazione del linguaggio è più vicino alle caratteristiche delle macchine che interpreteranno il codice, sia esso reale o virtuale.

Un altro punto è se i paradigmi implementati nella lingua siano chiusi al tipo di problema programmato. È prevedibile che un linguaggio di programmazione specializzato per paradigmi di programmazione specifici compilerà in modo molto efficiente funzionalità correlate a quel paradigma. Quindi la scelta di un linguaggio di programmazione può dipendere, per chiarezza e velocità, dalla scelta di un linguaggio di programmazione adattato al tipo di problema da programmare.

La popolarità di C per la programmazione del sistema è probabilmente dovuta al fatto che C è vicino all'architettura della macchina e che la programmazione del sistema è direttamente correlata a tale architettura.

Alcuni altri problemi verranno programmati più facilmente, con un'esecuzione più rapida utilizzando i linguaggi di programmazione logica e risoluzione dei vincoli .

I sistemi reattivi complessi possono essere programmati in modo molto efficiente con linguaggi di programmazione sincrona specializzati come Esterel che incorpora conoscenze molto specializzate su tali sistemi e genera codice molto veloce.

O per fare un esempio estremo, alcune lingue sono altamente specializzate, come i linguaggi di descrizione della sintassi usati per programmare i parser. Un generatore di parser non è altro che un compilatore per tali lingue. Naturalmente, Turing non è completo, ma questi compilatori sono estremamente utili per la loro specialità: produrre programmi di analisi efficienti. Poiché il dominio della conoscenza è limitato, le tecniche di ottimizzazione possono essere molto specializzate e messe a punto molto finemente. Questi generatori di parser sono di solito molto meglio di ciò che si potrebbe ottenere scrivendo codice in un'altra lingua. Esistono molti linguaggi altamente specializzati con compilatori che producono codice eccellente e veloce per una classe ristretta di problemi.

Pertanto, quando si scrive un sistema di grandi dimensioni, può essere consigliabile non fare affidamento su una sola lingua, ma scegliere la lingua migliore per i diversi componenti del sistema. Ciò, naturalmente, solleva problemi di compatibilità.

Un altro punto che conta spesso è semplicemente l'esistenza di librerie efficienti per gli argomenti programmati.

Infine, la velocità non è l'unico criterio e potrebbe essere in conflitto con altri criteri come la sicurezza del codice (ad esempio in caso di input errato o resilienza agli errori di sistema), uso della memoria, facilità di programmazione (sebbene la compatibilità del paradigma possa effettivamente aiutare ), dimensione del codice oggetto, manutenibilità del programma, ecc.

La velocità non è sempre il parametro più importante. Inoltre, potrebbe essere necessario assumere diverse forme, come la complessità che può essere una complessità media o una complessità peggiore. Inoltre, in un sistema di grandi dimensioni come in un programma più piccolo, ci sono parti in cui la velocità è fondamentale e altre in cui conta poco. E non è sempre facile determinarlo in anticipo.


Grazie. È qualcosa del genere. Stavo cercando. Era difficile trovare materiale per l'argomento. Questo chiarito abbastanza.
Rodrigo Valente,

@ RodrigoAraújoValente Grazie, potresti voler guardare questa domanda . Una visione estremista è che un compilatore per il linguaggio L può semplicemente raggruppare un interprete per L con il codice sorgente del programma, senza fare nulla. Quindi puoi fare di meglio cercando di ottimizzare il calcolo del pacchetto (valutazione parziale). Più ottimizzi e più veloce sarà la tua lingua. La domanda è quindi: cosa può aiutarti a ottimizzare? Le risposte possono includere una buona conoscenza di argomenti specifici, aiuto da parte del programmatore, analisi sofisticate, ecc ...
Babou

Per "stesso bytecode" suppongo che tu intenda "lo stesso tipo di rappresentazione eseguibile". Ovviamente eseguibili identici funzioneranno alla stessa velocità (assumendo lo stesso sistema di esecuzione). (Probabilmente l'avrei guardato da una prospettiva di informazione / comunicazione. In teoria , un programmatore può sapere tutto sul programma e sull'hardware mentre un linguaggio spesso limita la comunicazione (limitando quali trasformazioni sono consentite o utili) e il compilatore potrebbe non sapere dettagli microarchitetturali La compilazione e lo sviluppo del compilatore sono analoghi allo sviluppo e alla formazione ...
Paul A. Clayton

Ciò si inserisce quindi in ciò che gli umani sono generalmente bravi (ad esempio, riconoscendo gli schemi generali) rispetto a quali computer sono in genere bravi (ad esempio, la tenuta dei registri e "l'aritmetica"). Inoltre, la comunicazione delle informazioni di runtime è spesso più intuitiva per i computer (l'ottimizzazione guidata dal profilo può ovviare alla mancanza di informazioni comunicate attraverso il linguaggio di programmazione). L'ottimizzazione dinamica non sarebbe pratica con i programmatori umani ...
Paul A. Clayton,

(anche se si potrebbe dire lo stesso della scrittura di tutto il software in assembly da parte di programmatori esperti che prendono di mira hardware specifico, al momento dell'ottimizzazione del software non solo l'hardware sarebbe cambiato ma il software sarebbe obsoleto).)
Paul A. Clayton

16

Mentre tutto alla fine viene eseguito sulla CPU * , ci sono varie differenze tra le diverse lingue. Ecco alcuni esempi.

Lingue interpretate Alcune lingue vengono interpretate anziché compilate , ad esempio Python, Ruby e Matlab. Ciò significa che il codice Python e Ruby non viene compilato in codice macchina, ma viene interpretato al volo. È possibile compilare Python e Ruby su una macchina virtuale (vedere il punto successivo). Vedi anche questa domanda . L'interpretazione è generalmente più lenta del codice compilato per vari motivi. Non solo l'interpretazione può essere lenta, ma è anche più difficile fare ottimizzazioni. Tuttavia, se il codice trascorre la maggior parte del tempo in funzioni di libreria (il caso di Matlab), le prestazioni non ne risentiranno.

Macchina virtuale Alcuni linguaggi sono compilati in bytecode , un "codice macchina" inventato che viene poi interpretato. Gli esempi per eccellenza sono Java e C #. Mentre il bytecode può essere convertito in codice macchina al volo, il codice probabilmente funzionerà ancora più lentamente. Nel caso di Java, per la portabilità viene utilizzata una macchina virtuale. Nel caso di C #, potrebbero esserci altri problemi come la sicurezza.

Spese generali Alcune lingue scambiano efficienza per sicurezza. Ad esempio, alcune versioni di Pascal verificherebbero che non si accede a un array fuori limite. Il codice C # è "gestito" e questo ha un costo. Un altro esempio comune è la garbage collection, che consente di risparmiare tempo per il programmatore ma non è efficiente come la gestione della memoria. Esistono altre fonti di sovraccarico come l'infrastruttura per la gestione delle eccezioni o per supportare la programmazione orientata agli oggetti.

* In effetti, oggigiorno i sistemi ad alte prestazioni eseguono anche codice su GPU e persino su FPGA.


Quindi, fondamentalmente, se ho bisogno di più prestazioni, dovrei scegliere le lingue compilate? E i paradigmi? C'è un motivo per scegliere funzionale anziché oop o viceversa?
Rodrigo Valente,

La tua osservazione sulla raccolta dei rifiuti è un po 'semplicistica. Non è sempre decidibile staticamente quando la memoria allocata non viene più utilizzata. Anche se decidibile, può essere molto difficile determinare senza commettere errori. Pertanto, GC è talvolta necessario e spesso più sicuro (come il controllo dei limiti dell'array). Inoltre, può essere combinato con una versione esplicita.
babou,

@ RodrigoAraújoValente Dipende. Il codice scadente viene spesso compilato in codice scadente. Forse il codice che puoi scrivere in Python è in realtà più veloce del codice che puoi scrivere in C.
Raffaello

nit: come spiegato dalla domanda a cui ti sei collegato, Python non è in realtà interpretato "al volo" :)
Eevee

Nessuna delle lingue menzionate nella sezione interpretata viene interpretata al volo. Python è compilato in bytecode, Ruby è stato compilato in AST, ma credo che ora sia compilato in bytecode. Matlab, credo che ora sia effettivamente compilato JIT. In realtà, non conosco alcuna implementazione di linguaggio non di nicchia che interpreta le cose al volo piuttosto che almeno compilare un qualche tipo di rappresentazione di macchina virtuale.
Winston Ewert,

5

Ci sono diversi fattori per scegliere X invece di Y, come

  • Facilità di apprendimento
  • Facilità di comprensione
  • Velocità di sviluppo
  • Aiutare con l'applicazione del codice corretto
  • Prestazioni del codice compilato
  • Ambienti di piattaforma supportati
  • portabilità
  • Appropriato

Alcuni linguaggi sono adatti per lo sviluppo di progetti aziendali come C # o Python, ma altri sono buoni per la programmazione di sistemi come C ++.

È necessario determinare sotto quale piattaforma lavorerai e quale applicazione creerai.


1
Quindi, come posso determinare le prestazioni del codice compilato? Questo è fondamentalmente ciò che mi ha fatto fare questa domanda.
Rodrigo Valente,

6
Questa risposta ha certamente un buon consiglio, ma non riesco a vedere come risponde alla domanda, che riguarda la velocità come fattore di scelta per una lingua.
babou,

@ RodrigoAraújoValente Potrebbero anche non essere compilati codici (se la tua lingua viene interpretata).
Raffaello

1
Potresti voler aggiungere "Buone librerie" e "Buoni strumenti".
Raffaello

@ RodrigoAraújoValente Lo esegui e lo profili.
Kyle

2

Il linguaggio di programmazione "più veloce" che puoi ottenere con qualsiasi piattaforma è il linguaggio assembly per il chipset con cui hai a che fare. A quel livello non ci sono traduzioni. Tuttavia, è necessario avere una certa conoscenza di come il chipset esegue le istruzioni, specialmente quelle che possono fare cose in parallelo.

La conversione da C a assembly è molto "superficiale" che è vicina all'uno a uno ma è più leggibile. Tuttavia ha molti livelli sopra di esso grazie alle librerie standard per migliorare la portabilità. Non ci sono molte cose che il compilatore dovrebbe fare per arrivare al codice assembly e le ottimizzazioni più forti sono generalmente lì per apportare modifiche specifiche alla macchina.

C ++ aggiunge un linguaggio più ricco. Tuttavia, poiché il linguaggio aggiunge tanta complessità, diventa più difficile per il compilatore creare un codice ottimale per la piattaforma.

Quindi andiamo dall'altra parte della scala. Lingue interpretate. Questi tendono ad essere i più lenti perché oltre a fare il lavoro c'è un po 'di tempo speso per analizzare il codice e convertirlo in chiamate macchina.

Quindi abbiamo quelli in mezzo. Generalmente hanno un livello di macchina virtuale ottimizzato per la piattaforma. E il compilatore creerà il codice per l'esecuzione della macchina virtuale. A volte questo accade contemporaneamente come perl o pascal o ruby ​​o Python. O in diverse fasi come java.

Alcune di queste macchine virtuali aggiungono l'idea di un compilatore JIT che accelera anche il runtime creando codice a livello di macchina anziché tradurre il codice byte intermedio.

Alcune macchine virtuali sono di basso livello che consentono una minore traduzione dal codice byte al codice macchina. Che velocizzano le cose mantenendo la portabilità.


Storicamente, C è stato progettato per consentire una facile traduzione in codice macchina. In misura crescente, tuttavia, la trasformazione di C in un codice C efficiente richiede che un compilatore capisca ciò che un programmatore stava cercando di fare e quindi tradusse quell'intenzione in codice macchina. Ad esempio, storicamente l'equivalente del codice macchina di *p++=*q++;su molte macchine sarebbe stato più veloce di array1[i]=array2[i];ma su molti processori è spesso vero il contrario e quindi i compilatori potrebbero finire per convertire il precedente stile di codice in quest'ultimo - quasi una conversione "superficiale".
supercat

Questo di solito se lo fai -O0non farà alcuna ottimizzazione. Le ottimizzazioni sono un bonus che si ottiene con il compilatore, ma la lingua in sé può tradursi vicino a uno a uno in assembly.
Archimede Trajano,

2

Un punto che non è stato ancora menzionato è che in alcune lingue, l'esecuzione dello stesso codice più volte eseguirà sempre la stessa sequenza di azioni; il computer quindi deve solo determinare una volta che cosa dovrebbe fare la sezione di codice. Uno dei principali vantaggi del dialetto "usa rigorosamente" di JavaScript è che una volta che il motore JavaScript ha capito cosa fa un pezzo di codice, può usare queste informazioni alla successiva esecuzione; senza "usare rigoroso", non può.

Ad esempio, in assenza di "usa rigoroso", un pezzo di codice come:

function f() { return x; }

può restituire una variabile X nel contesto di chiamata immediata, se presente, oppure una variabile X da un contesto di chiamata esterno, oppure può restituire Undefined. Peggio ancora, in un ciclo come:

for (i=0; i<40; i+=1) { g(i); }

non c'è modo per il motore JavaScript di sapere cosa g()potrebbe fare con i[o a gse stesso per quella materia. Poiché go ipotrebbe legittimamente trasformarsi iin una stringa, il motore JavaScript non può semplicemente utilizzare un'aggiunta numerica e un confronto numerico nel ciclo, ma deve ogni passaggio attraverso il ciclo controllare per vedere se una delle chiamate di funzione ha fatto qualcosa per io una volta, piuttosto che doverle cercare ad ogni passaggio del ciclo: un notevole risparmio di tempo.g . Al contrario, nel dialetto "usa rigorosamente" [un po 'sano "il motore JavaScipt può esaminare il codice sopra e sapere che ogni passaggio attraverso il ciclo utilizzerà la stessa variabile numerica e invocherà la stessa funzione. Dovrà quindi identificarsii e funzionareg


2

Bene, ci sono alcune risposte abbastanza professionali qui, questa non è vicina a loro ma potrebbe essere intuitiva per te.

Potresti aver sentito molte volte che quando devi eseguire un'attività il più rapidamente possibile, vorrai scrivere il codice che lo esegue in assembly. Questo perché esegui solo i comandi di cui hai effettivamente bisogno per completare l'attività e nient'altro. Mentre su un linguaggio di alto livello è possibile implementare questa attività in più righe, il compilatore deve ancora tradurli in linguaggio macchina. Questa traduzione non è sempre minimalista in quanto potresti scriverla direttamente. Ciò significa che la macchina impiegherà molti orologi per eseguire comandi che è possibile risparmiare.

Sebbene i compilatori siano molto sofisticati oggi non sono ancora efficaci come possono essere i migliori programmatori di assembly.

Continuando in questa direzione, quei comandi non necessari crescono nella loro quantità (di solito) man mano che la lingua è di livello superiore. (questo non è vero al 100% per tutte le lingue di alto livello)

Quindi, per me, la lingua X è più veloce della lingua Y (in fase di esecuzione) se per un determinato codice, il codice macchina di X è più corto di quello di Y.


Il montaggio è totalmente roccioso. Ci vuole però un vero artista per superare i migliori compilatori.
Johan - ripristina Monica

1

È difficile rispondere a questa domanda in modo definitivo perché è così complesso e multidimensionale (è quasi come confrontare le marche di auto con criteri diversi) ma ci sono nuovi studi scientifici tra cui un eccellente repository di codici noto come codice Rosetta , ( panoramica di Wikipedia ). Questo sondaggio del 2014 di Nanz e Furia studia questa domanda in modo abbastanza definitivo e scientifico sulla base dei seguenti criteri tipici e di una rara analisi quantitativa di qualità tipicamente soggettive del codice. L'abstract contiene alcune scoperte e generalizzazioni oggettivamente fondate. (Spero che altri studi basati su questi risultati possano essere fatti in futuro.)

  • RQ1. Quali linguaggi di programmazione rendono il codice più conciso?

  • RQ2. Quali linguaggi di programmazione vengono compilati in file eseguibili più piccoli?

  • RQ3. Quali linguaggi di programmazione hanno migliori prestazioni in fase di esecuzione?

  • RQ4. Quali linguaggi di programmazione usano la memoria in modo più efficiente?

  • RQ5. Quali linguaggi di programmazione sono meno soggetti a guasti?

Riassunto: a volte i dibattiti sui linguaggi di programmazione sono più religiosi che scientifici. Le domande su quale linguaggio sia più conciso o efficiente o che renda gli sviluppatori più produttivi vengono discusse con fervore e le loro risposte sono troppo spesso basate su aneddoti e credenze prive di fondamento. In questo studio, utilizziamo il potenziale di ricerca in gran parte non sfruttato di Rosetta Code, un repository di codice di soluzioni per attività di programmazione comuni in vari linguaggi, che offre un ampio set di dati per l'analisi. Il nostro studio si basa su 7.087 programmi di soluzione corrispondenti a 745 compiti in 8 linguaggi ampiamente utilizzati che rappresentano i principali paradigmi di programmazione (procedurale: C and Go; orientato agli oggetti: C # e Java; funzionale: F # e Haskell; scripting: Python e Ruby). La nostra analisi statistica rivela, in particolare, che: i linguaggi funzionali e di scripting sono più concisi dei linguaggi procedurali e orientati agli oggetti; C è difficile da battere quando si tratta di velocità raw su input di grandi dimensioni, ma le differenze di prestazioni rispetto a input di dimensioni moderate sono meno pronunciate e consentono anche alle lingue interpretate di essere competitive; i linguaggi compilati fortemente tipizzati, in cui è possibile rilevare più difetti in fase di compilazione, sono meno inclini a fallimenti di runtime rispetto ai linguaggi interpretati o di tipo debole. Discutiamo delle implicazioni di questi risultati per sviluppatori, progettisti di lingue ed educatori. dove è possibile rilevare più difetti al momento della compilazione, sono meno inclini a errori di runtime rispetto ai linguaggi interpretati o di tipo debole. Discutiamo delle implicazioni di questi risultati per sviluppatori, progettisti di lingue ed educatori. dove è possibile rilevare più difetti al momento della compilazione, sono meno inclini a errori di runtime rispetto ai linguaggi interpretati o di tipo debole. Discutiamo delle implicazioni di questi risultati per sviluppatori, progettisti di lingue ed educatori.


0

I linguaggi informatici sono solo un'astrazione di comandi per spiegare al computer cosa fare.

Puoi persino scrivere nel linguaggio del computer Pythone compilarlo con un compilatore C (cython).

Tenendo presente questo, la velocità dei linguaggi informatici non può essere confrontata.

Ma puoi confrontare i compilatori per la stessa lingua fino a un certo punto. Ad esempio GNU Ccompilatore contro Intel Ccompilatore. (Cerca il benchmark del compilatore)


2
Se vuoi fare commenti sulla domanda, utilizza la casella dei commenti, non la tua risposta. Si noti che questo è Computer Science Stack Exchange e l'informatica non è programmazione, così come la letteratura non è word-processing. Domande di programmazione in diretta su Software Engineering o Stack Overflow .
David Richerby,
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.