JIT vs compilatore statico
Come già detto nei post precedenti, JIT può compilare IL / bytecode in codice nativo in fase di runtime. Il costo è stato menzionato, ma non alla sua conclusione:
JIT ha un grosso problema è che non può compilare tutto: la compilazione JIT richiede tempo, quindi JIT compilerà solo alcune parti del codice, mentre un compilatore statico produrrà un binario nativo completo: per alcuni tipi di programmi, lo statico il compilatore supererà facilmente il JIT.
Ovviamente, C # (o Java, o VB) è solitamente più veloce nel produrre una soluzione valida e robusta rispetto al C ++ (se non altro perché C ++ ha una semantica complessa e la libreria standard C ++, sebbene interessante e potente, è piuttosto scarsa se confrontata con la versione completa ambito della libreria standard da .NET o Java), quindi di solito la differenza tra C ++ e .NET o Java JIT non sarà visibile alla maggior parte degli utenti e per quei binari che sono critici, beh, puoi comunque chiamare l'elaborazione C ++ da C # o Java (anche se questo tipo di chiamate native può essere abbastanza costoso di per sé) ...
Metaprogrammazione C ++
Notare che di solito si confronta il codice runtime C ++ con il suo equivalente in C # o Java. Ma C ++ ha una caratteristica che può superare Java / C # fuori dagli schemi, ovvero la metaprogrammazione del modello: l'elaborazione del codice verrà eseguita al momento della compilazione (quindi, aumentando notevolmente il tempo di compilazione), risultando in zero (o quasi zero) runtime.
Ho ancora visto un effetto della vita reale su questo (ho giocato solo con i concetti, ma a quel punto la differenza era di secondi di esecuzione per JIT e zero per C ++), ma vale la pena menzionarlo, insieme al fatto che la metaprogrammazione del modello non lo è banale...
Modifica 2011-06-10: In C ++, giocare con i tipi viene eseguito in fase di compilazione, il che significa produrre codice generico che chiama codice non generico (ad esempio un parser generico dalla stringa al tipo T, chiamando l'API della libreria standard per i tipi T che riconosce, e rendere il parser facilmente estensibile dal suo utente) è molto semplice e molto efficiente, mentre l'equivalente in Java o C # è doloroso nella migliore delle ipotesi da scrivere, e sarà sempre più lento e risolto in fase di esecuzione anche quando i tipi sono noti in fase di compilazione, il che significa che la tua unica speranza è che il JIT inline l'intera cosa.
...
Modifica 2011-09-20: Il team dietro Blitz ++ ( Homepage , Wikipedia ) è andato in questo modo e, a quanto pare, il loro obiettivo è raggiungere le prestazioni di FORTRAN sui calcoli scientifici spostandosi il più possibile dall'esecuzione in runtime al tempo di compilazione, tramite la metaprogrammazione del modello C ++ . Così il " Ho eppure così vedere un vero e proprio effetto vita su questa " parte ho scritto sopra a quanto pare fa esistere nella vita reale.
Utilizzo della memoria C ++ nativa
C ++ ha un utilizzo della memoria diverso da Java / C # e, quindi, presenta vantaggi / difetti diversi.
Indipendentemente dall'ottimizzazione JIT, nulla andrà veloce come l'accesso diretto del puntatore alla memoria (ignoriamo per un momento le cache del processore, ecc.). Quindi, se hai dati contigui in memoria, accedervi tramite puntatori C ++ (cioè puntatori C ... Diamo a Caesar ciò che gli è dovuto) andrà molto più velocemente che in Java / C #. E C ++ ha RAII, che rende molte elaborazioni molto più semplici rispetto a C # o persino a Java. C ++ non ha bisogno using
di definire l'ambito dell'esistenza dei suoi oggetti. E C ++ non ha una finally
clausola. Questo non è un errore.
:-)
E nonostante le strutture di tipo primitivo C #, gli oggetti C ++ "in pila" non costeranno nulla in fase di allocazione e distruzione e non avranno bisogno di GC per lavorare in un thread indipendente per eseguire la pulizia.
Per quanto riguarda la frammentazione della memoria, gli allocatori di memoria nel 2008 non sono i vecchi allocatori di memoria del 1980 che di solito vengono confrontati con un GC: l'allocazione C ++ non può essere spostata in memoria, vero, ma poi, come su un filesystem Linux: chi ha bisogno del disco rigido deframmentazione quando la frammentazione non avviene? L'utilizzo dell'allocatore giusto per l'attività giusta dovrebbe far parte del toolkit per sviluppatori C ++. Ora, scrivere gli allocatori non è facile, quindi la maggior parte di noi ha cose migliori da fare e, per la maggior parte dell'uso, RAII o GC è più che sufficiente.
Modifica 2011-10-04: per esempi su allocatori efficienti: sulle piattaforme Windows, a partire da Vista, l' Heap a bassa frammentazione è abilitato per impostazione predefinita. Per le versioni precedenti, LFH può essere attivato chiamando la funzione WinAPI HeapSetInformation ). Su altri sistemi operativi, vengono forniti allocatori alternativi (vederehttps://secure.wikimedia.org/wikipedia/en/wiki/Malloc per un elenco)
Ora, il modello di memoria sta diventando un po 'più complicato con l'avvento della tecnologia multicore e multithreading. In questo campo, immagino che .NET abbia il vantaggio e Java, mi è stato detto, ha tenuto il sopravvento. È facile per alcuni hacker "on the bare metal" lodare il suo codice "near the machine". Ma ora, è abbastanza più difficile produrre un assembly migliore a mano che lasciare che il compilatore faccia il suo lavoro. Per C ++, il compilatore è diventato solitamente migliore dell'hacker da un decennio. Per C # e Java, questo è ancora più semplice.
Tuttavia, il nuovo standard C ++ 0x imporrà un semplice modello di memoria ai compilatori C ++, che standardizzerà (e quindi semplificherà) il codice multiprocessing / parallelo / threading efficace in C ++ e renderà le ottimizzazioni più facili e sicure per i compilatori. Ma poi, vedremo tra un paio d'anni se le sue promesse saranno mantenute.
C ++ / CLI contro C # / VB.NET
Nota: in questa sezione, sto parlando di C ++ / CLI, ovvero il C ++ ospitato da .NET, non il C ++ nativo.
La scorsa settimana, ho seguito una formazione sull'ottimizzazione .NET e ho scoperto che il compilatore statico è comunque molto importante. Importante quanto JIT.
Lo stesso codice compilato in C ++ / CLI (o il suo predecessore, Managed C ++) potrebbe essere volte più veloce dello stesso codice prodotto in C # (o VB.NET, il cui compilatore produce lo stesso IL di C #).
Perché il compilatore statico C ++ era molto meglio per produrre codice già ottimizzato rispetto a C #.
Ad esempio, l'inlining di funzioni in .NET è limitato alle funzioni il cui bytecode è inferiore o uguale a 32 byte di lunghezza. Quindi, un po 'di codice in C # produrrà una funzione di accesso da 40 byte, che non sarà mai inline da JIT. Lo stesso codice in C ++ / CLI produrrà una funzione di accesso di 20 byte, che sarà inline da JIT.
Un altro esempio sono le variabili temporanee, che vengono semplicemente compilate dal compilatore C ++ pur essendo ancora menzionate nell'IL prodotto dal compilatore C #. L'ottimizzazione della compilazione statica C ++ risulterà in meno codice, quindi autorizza di nuovo un'ottimizzazione JIT più aggressiva.
La ragione di ciò è stata ipotizzata per essere il fatto che il compilatore C ++ / CLI ha tratto vantaggio dalle vaste tecniche di ottimizzazione del compilatore nativo C ++.
Conclusione
Amo il C ++.
Ma per quanto ne so, C # o Java sono tutto sommato una scommessa migliore. Non perché siano più veloci del C ++, ma perché quando si sommano le loro qualità, finiscono per essere più produttivi, necessitano di meno formazione e hanno librerie standard più complete del C ++. E come per la maggior parte dei programmi, le loro differenze di velocità (in un modo o nell'altro) saranno trascurabili ...
Modifica (2011-06-06)
La mia esperienza su C # /. NET
Ora ho 5 mesi di codifica C # professionale quasi esclusiva (che si aggiunge al mio CV già pieno di C ++ e Java, e un tocco di C ++ / CLI).
Ho giocato con WinForms (Ahem ...) e WCF (cool!), E WPF (Cool !!!! Sia tramite XAML che raw C #. WPF è così facile che credo che Swing non possa essere paragonato ad esso) e C # 4.0.
La conclusione è che mentre è più facile / veloce produrre un codice che funzioni in C # / Java che in C ++, è molto più difficile produrre un codice forte, sicuro e robusto in C # (e ancora più difficile in Java) che in C ++. Le ragioni abbondano, ma si possono riassumere in:
- I generici non sono potenti quanto i modelli ( prova a scrivere un metodo Parse generico efficiente (dalla stringa a T) o un equivalente efficiente di boost :: lexical_cast in C # per capire il problema )
- RAII rimane impareggiabile ( GC può ancora perdere (sì, ho dovuto gestire quel problema) e gestirà solo la memoria. Anche il C #
using
non è così facile e potente perché scrivere una corretta implementazione di Dispose è difficile )
- C #
readonly
e Java final
non sono utili quanto quelli di C ++const
( non c'è modo di esporre dati complessi di sola lettura (un albero di nodi, per esempio) in C # senza un lavoro eccezionale, mentre è una funzionalità incorporata di C ++. I dati immutabili sono una soluzione interessante , ma non tutto può essere reso immutabile, quindi non è nemmeno abbastanza, di gran lunga ).
Quindi, C # rimane un linguaggio piacevole fintanto che si desidera qualcosa che funzioni, ma un linguaggio frustrante nel momento in cui si desidera qualcosa che funzioni sempre e in sicurezza .
Java è ancora più frustrante, in quanto ha gli stessi problemi di C # e altro ancora: mancando l'equivalente della using
parola chiave di C # , un mio collega molto esperto ha trascorso troppo tempo assicurandosi che le sue risorse fossero liberate correttamente, mentre l'equivalente in C ++ avrebbe stato facile (usando distruttori e puntatori intelligenti).
Quindi immagino che l'aumento di produttività di C # / Java sia visibile per la maggior parte del codice ... fino al giorno in cui avrai bisogno che il codice sia il più perfetto possibile. Quel giorno conoscerai il dolore. (non crederai a ciò che viene richiesto dal nostro server e dalle app GUI ...).
Informazioni su Java lato server e C ++
Sono rimasto in contatto con i team server (ho lavorato 2 anni tra di loro, prima di tornare al team GUI), dall'altra parte dell'edificio, e ho imparato qualcosa di interessante.
Negli ultimi anni, la tendenza era che le app server Java fossero destinate a sostituire le vecchie app server C ++, poiché Java ha molti framework / strumenti ed è facile da mantenere, distribuire, ecc. Ecc.
... Fino a quando il problema della bassa latenza non ha sollevato la sua brutta testa negli ultimi mesi. Quindi, le app del server Java, indipendentemente dall'ottimizzazione tentata dal nostro esperto team Java, hanno perso la gara in modo semplice e pulito contro il vecchio server C ++ non ottimizzato.
Attualmente, la decisione è di mantenere i server Java per un uso comune in cui le prestazioni, pur essendo ancora importanti, non sono interessate dall'obiettivo di bassa latenza e ottimizzare in modo aggressivo le applicazioni server C ++ già più veloci per esigenze di bassa e bassissima latenza.
Conclusione
Niente è semplice come previsto.
Java, e ancora di più C #, sono linguaggi interessanti, con librerie e framework standard estesi, in cui è possibile programmare velocemente e ottenere risultati molto presto.
Ma quando hai bisogno di potenza pura, ottimizzazioni potenti e sistematiche, forte supporto per il compilatore, potenti funzionalità di linguaggio e sicurezza assoluta, Java e C # rendono difficile vincere le ultime percentuali di qualità mancanti ma critiche, devi rimanere al di sopra della concorrenza.
È come se avessi bisogno di meno tempo e sviluppatori meno esperti in C # / Java che in C ++ per produrre codice di qualità media, ma d'altra parte, nel momento in cui avevi bisogno di codice di qualità eccellente per perfezionare, è stato improvvisamente più facile e veloce ottenere i risultati proprio in C ++.
Naturalmente, questa è la mia percezione, forse limitata ai nostri bisogni specifici.
Tuttavia, è ciò che accade oggi, sia nei team della GUI che nei team lato server.
Ovviamente aggiornerò questo post se succede qualcosa di nuovo.
Modifica (2011-06-22)
"Troviamo che per quanto riguarda le prestazioni, C ++ vince con un ampio margine. Tuttavia, ha anche richiesto gli sforzi di ottimizzazione più estesi, molti dei quali sono stati eseguiti a un livello di sofisticazione che non sarebbe stato disponibile per il programmatore medio.
[...] La versione Java era probabilmente la più semplice da implementare, ma la più difficile da analizzare per le prestazioni. Nello specifico, gli effetti della raccolta dei rifiuti erano complicati e molto difficili da regolare ".
fonti:
Modifica (2011-09-20)
"La parola corrente su Facebook è che" il codice C ++ scritto in modo ragionevole funziona velocemente " , il che sottolinea l'enorme sforzo speso per ottimizzare PHP e codice Java. Paradossalmente, il codice C ++ è più difficile da scrivere che in altri linguaggi, ma il codice efficiente è un molto più facile [scrivere in C ++ che in altri linguaggi]. "
- Herb Sutter su // build / , citando Andrei Alexandrescu
fonti: