Cosa regola la "velocità" di un linguaggio di programmazione?
Non esiste la "velocità" di un linguaggio di programmazione. Esiste solo la velocità di un particolare programma scritto da un particolare programmatore eseguito da una particolare versione di una particolare implementazione di un particolare motore di esecuzione in esecuzione in un determinato ambiente.
Ci possono essere enormi differenze di prestazioni nell'esecuzione dello stesso codice scritto nella stessa lingua sullo stesso computer utilizzando implementazioni diverse. O anche usando versioni diverse della stessa implementazione. Ad esempio, l'esecuzione dello stesso identico benchmark ECMAScript sulla stessa macchina utilizzando una versione di SpiderMonkey di 10 anni fa rispetto a una versione di quest'anno produrrà probabilmente un aumento delle prestazioni tra 2 × –5 ×, forse anche 10 ×. Ciò significa quindi che ECMAScript è 2 volte più veloce di ECMAScript, perché l'esecuzione dello stesso programma sulla stessa macchina è 2 volte più veloce con l'implementazione più recente? Non ha senso.
Ha qualcosa a che fare con la gestione della memoria?
Non proprio.
Perché succede?
Risorse. I soldi. Probabilmente Microsoft impiega più persone a preparare il caffè per i programmatori di compilatori rispetto all'intera comunità di PHP, Ruby e Python che riunisce persone che lavorano sulle loro macchine virtuali.
Per più o meno qualsiasi caratteristica di un linguaggio di programmazione che influisce in qualche modo sulle prestazioni, esiste anche una soluzione. Ad esempio, C (sto usando C qui come stand-in per una classe di linguaggi simili, alcuni dei quali esistevano anche prima di C) non è sicuro per la memoria, in modo che più programmi C in esecuzione contemporaneamente possano calpestare memoria reciproca. Quindi, inventiamo la memoria virtuale e facciamo passare tutti i programmi C attraverso uno strato di indiretta in modo che possano far finta di essere gli unici in esecuzione sulla macchina. Tuttavia, è lento, quindi inventiamo la MMU e implementiamo la memoria virtuale nell'hardware per accelerarla.
Ma! Le lingue sicure per la memoria non hanno bisogno di tutto questo! Avere memoria virtuale non li aiuta un po '. In realtà, è peggio: non solo la memoria virtuale non aiuta i linguaggi a prova di memoria, la memoria virtuale, anche se implementata in hardware, influisce comunque sulle prestazioni. Può essere particolarmente dannoso per le prestazioni dei garbage collector (che è ciò che utilizza un numero significativo di implementazioni di linguaggi sicuri per la memoria).
Un altro esempio: le moderne CPU per uso generale mainstream utilizzano trucchi sofisticati per ridurre la frequenza dei mancati cache. Molti di questi trucchi equivalgono a tentare di prevedere quale codice verrà eseguito e quale memoria sarà necessaria in futuro. Tuttavia, per le lingue con un elevato grado di polimorfismo di runtime (ad es. Lingue OO) è davvero molto difficile prevedere tali schemi di accesso.
Ma esiste un altro modo: il costo totale dei mancati cache è il numero di mancati cache moltiplicato per il costo di una singola perdita cache. Le CPU mainstream cercano di ridurre il numero di errori, ma cosa succede se si potesse ridurre il costo di un singolo errore?
La CPU Azul Vega-3 è stata appositamente progettata per l'esecuzione di JVM virtualizzate e disponeva di una MMU molto potente con alcune istruzioni specializzate per aiutare la raccolta di rifiuti e il rilevamento di escape (l'equivalente dinamico dell'analisi di escape statica) e potenti controller di memoria e l'intero sistema potrebbe ancora fare progressi con oltre 20000 mancati cache mancanti in volo. Sfortunatamente, come la maggior parte delle CPU specifiche del linguaggio, il suo design è stato semplicemente esaurito e forzato dai "giganti" Intel, AMD, IBM e simili.
L'architettura della CPU è solo un esempio che ha un impatto su quanto sia facile o difficile avere un'implementazione ad alte prestazioni di un linguaggio. Un linguaggio come C, C ++, D, Rust che si adatta bene al moderno modello di programmazione della CPU tradizionale sarà più facile da rendere veloce rispetto a un linguaggio che deve "combattere" e aggirare la CPU, come Java, ECMAScript, Python, Ruby , PHP.
Davvero, è tutta una questione di soldi. Se si spendono le stesse quantità di denaro per sviluppare un algoritmo ad alte prestazioni in ECMAScript, un'implementazione ad alte prestazioni di ECMAScript, un sistema operativo ad alte prestazioni progettato per ECMAScript, una CPU ad alte prestazioni progettata per ECMAScript come è stata spesa nell'ultima decenni per accelerare le lingue di tipo C, quindi probabilmente vedrai pari prestazioni. È solo che, in questo momento, sono stati spesi molti più soldi per velocizzare i linguaggi di tipo C piuttosto che per velocizzare i linguaggi di tipo ECMAScript, e le ipotesi dei linguaggi di tipo C sono inserite nell'intero stack da MMU e CPU a sistemi operativi e sistemi di memoria virtuale fino a librerie e framework.
Personalmente, conosco meglio Ruby (che è generalmente considerato un "linguaggio lento"), quindi Hash
fornirò due esempi: la classe (una delle strutture di dati centrali in Ruby, un dizionario chiave-valore) nel Rubinius L'implementazione di Ruby è scritta in Ruby puro al 100% e ha circa le stesse prestazioni diHash
classe in YARV (l'implementazione più utilizzata), che è scritta in C. E c'è una libreria di manipolazione delle immagini scritta come estensione C per YARV, che ha anche una (lenta) Ruby "versione fallback" pura per implementazioni che don supporta C che utilizza una tonnellata di trucchi Ruby altamente dinamici e riflessivi; un ramo sperimentale di JRuby, che utilizza il framework di interpreti Truffle AST e il framework di compilazione Graal JIT di Oracle Labs, può eseguire quella "versione fallback" di Ruby con la stessa velocità con cui YARV può eseguire l'originale versione C altamente ottimizzata. Questo è semplicemente (beh, tutt'altro) raggiunto da alcune persone davvero intelligenti che fanno cose davvero intelligenti con ottimizzazioni di runtime dinamiche, compilazione JIT e valutazione parziale.