Perché C è così veloce e perché altre lingue non sono così veloci o più veloci? [chiuso]


209

Nell'ascolto del podcast StackOverflow, il jab continua a pensare che "veri programmatori" scrivano in C e che C è molto più veloce perché è "vicino alla macchina". Lasciando la precedente affermazione per un altro post, cosa c'è di speciale in C che gli permette di essere più veloce di altre lingue? O in altri termini: cosa impedisce ad altre lingue di essere in grado di compilare in binario che funziona alla stessa velocità di C?


6
Puoi elencare quale spettacolo particolare ne ha parlato? Mi piacerebbe sentirlo.
Giovanni Galbo,

2
Sono davvero sorpreso dalla risposta negativa a questa domanda (la maggior parte delle risposte ignora le differenze fondamentali tra le lingue compilate e interpretate, ecc., Conosco JIT yada yada yada) e quante persone stanno assumendo una posizione che "difende" il loro linguaggio preferito (i bisogni del ragazzo FORTRAN prendere una pillola).
Tim Ring

Non dimenticare il linguaggio assembly. Niente è più veloce o più compatto degli ex assemblati. Assembly è quasi puro binario, quindi è senza pregiudizi il linguaggio più veloce.
KKZiomek,

3
C è la più veloce perché è la velocità della luce e della relatività?
Incontra Taraviya il

Naturalmente è sbagliato che C sia il linguaggio di programma più veloce. Nessun linguaggio di programma di alcun tipo si avvicina alla velocità di FORTH. FORTH è usato per innescare bombe nucleari, è la lingua del programma sulla maggior parte dei satelliti, la lingua principale del programma nella Stazione Spaziale Internazionale e anche nel CERN e in ITER. Stavo confrontando la velocità tra Microsoft C (versioni diverse) e FORTH. YAWN to C ...
Scoobeedo Cool

Risposte:


200

Non c'è molto di speciale in C. Questo è uno dei motivi per cui è veloce.

Lingue più recenti che supportano la garbage collection , la digitazione dinamica e altre funzionalità che semplificano la scrittura dei programmi da parte del programmatore.

Il problema è che c'è un sovraccarico di elaborazione aggiuntivo che peggiorerà le prestazioni dell'applicazione. C non ha nulla di tutto ciò, il che significa che non c'è sovraccarico, ma ciò significa che il programmatore deve essere in grado di allocare memoria e liberarli per evitare perdite di memoria , e deve occuparsi della tipizzazione statica delle variabili.

Detto questo, molti linguaggi e piattaforme, come Java (con la sua Java Virtual Machine ) e .NET (con il suo Common Language Runtime) hanno migliorato le prestazioni nel corso degli anni con strumenti come la compilazione just-in-time che produce codice macchina nativo da bytecode per ottenere prestazioni più elevate.


3
la garbage collection può essere più veloce della gestione manuale della memoria (per programmi di breve durata e / o molta memoria). GC consente un'allocazione semplice e veloce e il programma non impiega tempo a deallocare le cose.
Kornel,

2
I programmi C generalmente allocano e deallocano la memoria secondo necessità. Questo è inefficiente. Una buona VM assegnerà e distribuirà in grandi blocchi, ottenendo grandi guadagni in termini di prestazioni in molti casi.
skaffman,

61
Non c'è nulla che impedisca a un programma C di eseguire la stessa allocazione in blocco e la garbage collection, oltre ad essere "difficile".
effimero

Molto ben detto, ma come Rob Allen ha detto, C fornisce anche meno astrazione rispetto a Java o .NET, risultando in una traduzione minore (che è meno vero oggi a causa della compilazione just-in-time (JIT) come hai detto)
Gab Royer

5
porneL, la gestione manuale e le allocazioni sensibili supereranno sempre qualsiasi sistema GC se usato correttamente e molta attenzione viene data, hai una conoscenza assoluta dei tuoi modelli di utilizzo, il GC no, inoltre i sistemi GC aggiungono sovraccarico
Ion Todirel

89

C'è un trade off che i designer C hanno fatto. Vale a dire, hanno preso la decisione di mettere la velocità al di sopra della sicurezza. C no

  • Controlla i limiti dell'indice dell'array
  • Verifica valori variabili non inizializzati
  • Verificare la presenza di perdite di memoria
  • Controlla la dereferenza del puntatore null

Quando si esegue l'indicizzazione in un array, in Java sono necessarie alcune chiamate di metodo nella macchina virtuale, il controllo associato e altri controlli di integrità. Questo è valido e assolutamente valido , perché aggiunge sicurezza dove è dovuto. Ma in C, anche le cose piuttosto banali non vengono messe in sicurezza. Ad esempio, C non richiede memcpy per verificare se le regioni da copiare si sovrappongono. E ' non è stato progettato come un linguaggio per programmare una grande applicazione aziendale.

Ma queste decisioni di progettazione sono non insetti nel linguaggio C . Sono progettate, in quanto consentono a compilatori e scrittori di biblioteche di ottenere tutte le prestazioni dal computer. Ecco lo spirito di C come spiega il documento C Rationale :

Il codice C può essere non portatile. Sebbene si sia sforzato di offrire ai programmatori l'opportunità di scrivere programmi veramente portatili, il Comitato non ha voluto forzare i programmatori a scrivere in modo portabile, per impedire l'uso di C come `` assemblatore di alto livello '': la capacità di scrivere su macchine specifiche il codice è uno dei punti di forza di C.

Mantenere lo spirito di C. Il Comitato ha mantenuto come obiettivo principale la conservazione dello spirito tradizionale di C. Ci sono molte sfaccettature dello spirito di C, ma l'essenza è un sentimento comunitario dei principi sottostanti su cui si basa il linguaggio C. Alcune delle sfaccettature dello spirito di C possono essere riassunte in frasi come

  • Fidati del programmatore.
  • Non impedire al programmatore di fare ciò che deve essere fatto.
  • Mantieni la lingua piccola e semplice.
  • Fornire un solo modo per eseguire un'operazione.
  • Renderlo veloce, anche se non è garantito che sia portatile.

L'ultimo proverbio ha bisogno di una piccola spiegazione. Il potenziale per un'efficace generazione di codice è uno dei punti di forza più importanti di C. Per aiutare a garantire che non si verifichi un'esplosione di codice per quella che sembra un'operazione molto semplice, molte operazioni sono definite come l'hardware della macchina target fa piuttosto che da una regola astratta generale. Un esempio di questa volontà di convivere con ciò che fa la macchina può essere visto nelle regole che regolano l'ampliamento degli oggetti char da usare nelle espressioni: se i valori degli oggetti char si allargano a quantità con o senza segno, in genere dipende da quale operazione di byte è maggiore efficiente sulla macchina target.


51
C verifica la presenza di un puntatore nullo deref bloccandosi violentemente :-). Occasionalmente verifica anche la presenza di indici di array fuori range e variabili non inizializzate rovinando i frame e i dati dello stack. Sfortunatamente li controlla in fase di esecuzione.
paxdiablo,

18
Non direi che C non è sicuro, il che suona come quello a cui stai accennando. Presuppone che tu non sia un idiota. Se punti una pistola verso il basso e ti spari in un piede, C ti obbligherà felicemente e ti lascerà farlo perché presume che tu sia più intelligente di quello che è. Non è necessariamente una brutta cosa.
Bob Somers,

19
@Bob: esattamente. Dire che C non è sicuro perché ti consente di fare cose pericolose è come dire che un'auto non è sicura perché ti permetterà di guidare da una scogliera. C è sicuro quanto la persona che sta guidando (ma ci sono molti conducenti non sicuri là fuori).
Robert Gamble,

5
Bob, fare bug come sovraccarichi del buffer non significa che sei un idiota. Significa solo che sei ancora umano. Mi rendo conto che C e C ++ non sono male (mi piacciono molto).
Johannes Schaub -

4
@ JohannesSchaub-litb C è perfetto per la programmazione di applicazioni su larga scala. Se uno trova difficile realizzare progetti più grandi di un mondo di ciao, allora il problema è con il programmatore, non con la lingua ...

75

Se passi un mese a costruire qualcosa in C che gira in 0,05 secondi, e io passo un giorno a scrivere la stessa cosa in Java, e gira in 0,10 secondi, allora C è davvero più veloce?

Ma per rispondere alla tua domanda, il codice C ben scritto sarà generalmente più veloce del codice ben scritto in altre lingue perché parte della scrittura del codice C "bene" include l'ottimizzazione manuale a livello di macchina.

Sebbene i compilatori siano davvero molto intelligenti, non sono ancora in grado di elaborare in modo creativo codice che compete con gli algoritmi massaggiati a mano (supponendo che le "mani" appartengano a un buon programmatore C).

Modificare:

Molti commenti sono sulla falsariga di "scrivo in C e non penso alle ottimizzazioni".

Ma per fare un esempio specifico da questo post :

In Delphi potrei scrivere questo:

function RemoveAllAFromB(a, b: string): string;
var
  before, after :string;
begin
  Result := b;
  if 0 < Pos(a,b) then begin
    before := Copy(b,1,Pos(a,b)-Length(a));
    after := Copy(b,Pos(a,b)+Length(a),Length(b));
    Result := before + after;
    Result := RemoveAllAFromB(a,Result);  //recursive
  end;
end;

e in CI scrivi questo:

char *s1, *s2, *result; /* original strings and the result string */
int len1, len2; /* lengths of the strings */
for (i = 0; i < len1; i++) {
   for (j = 0; j < len2; j++) {
     if (s1[i] == s2[j]) {
       break;
     }
   }
   if (j == len2) {  /* s1[i] is not found in s2 */
     *result = s1[i]; 
     result++; /* assuming your result array is long enough */
   }
}

Ma quante ottimizzazioni ci sono nella versione C? Prendiamo molte decisioni sull'implementazione a cui non penso nella versione di Delphi. Come viene implementata una stringa? A Delfi non lo vedo. In C, ho deciso che sarà un puntatore a un array di numeri interi ASCII, che chiamiamo caratteri. In C, testiamo l'esistenza del personaggio una alla volta. In Delphi, utilizzo Pos.

E questo è solo un piccolo esempio. In un programma di grandi dimensioni, un programmatore C deve prendere questo tipo di decisioni di basso livello con poche righe di codice. Aggiunge un eseguibile realizzato a mano e ottimizzato a mano.


46
Ad essere onesti, tuttavia, non c'è molto che richiederebbe un mese in C che richiederebbe solo un giorno in Java che richiede solo 0,05 secondi per essere eseguito (cioè un piccolo programma).
Dreamlax,

13
Ho programmato in C per anni e quasi mai devo fare nessuno dei tipi di ottimizzazioni a cui accenni. Ho portato un numero di programmi su C (principalmente da Perl) e generalmente vedo un aumento della velocità di 10 volte più e una riduzione significativa dell'utilizzo della memoria senza ottimizzazioni codificate a mano.
Robert Gamble,

1
Naturalmente ci sono alcune cose che possono richiedere molto più tempo per programmare in C a causa della mancanza di strutture esistenti, quindi è un compromesso tra le prestazioni del computer e le prestazioni del programmatore (tra le altre cose), motivo per cui non tutti programmiamo tutto in C.
Robert Gamble,

4
Ho creato programmi C ++ che elaborano migliaia di righe di dati in meno tempo rispetto all'avvio dei programmi Java o .NET. È una delle frustrazioni che ho con le lingue più moderne. C è ottimo per i programmi lean che richiedono requisiti minimi di runtime. PowerBasic è ottimo anche per questo.
Bruceatk,

35
Stai dicendo che un programma che impiega un mese in C ed è due volte più veloce di un programma scritto in Java che impiega solo un giorno per scrivere non vale la pena? Cosa succede se quel programma deve essere eseguito oltre 500.000.000 di volte al giorno? Essere due volte più veloce è incredibilmente significativo. Se funziona su migliaia o milioni di CPU, i risparmi sui costi del mese aggiuntivo di sviluppo per raggiungere il doppio delle prestazioni saranno enormi. Fondamentalmente, devi conoscere / comprendere le dimensioni della tua distribuzione prima di scegliere la piattaforma di sviluppo.
Nicerobot,

49

Non ho visto già, così lo dirò: C tende ad essere più veloce perché quasi tutto il resto è scritto in C .

Java è costruito su C, Python è costruito su C (o Java, o .NET, ecc.), Perl è, ecc. Il sistema operativo è scritto in C, le macchine virtuali sono scritte in C, i compilatori sono scritti in C, gli interpreti sono scritti in C. Alcune cose sono ancora scritte in linguaggio Assembly, che tende ad essere ancora più veloce. Sempre più cose vengono scritte in qualcos'altro, che è esso stesso scritto in C.

Ogni istruzione scritta in altre lingue (non in Assembly) viene in genere implementata come diverse istruzioni in C, che vengono compilate in codice macchina nativo. Dato che quelle altre lingue tendono ad esistere al fine di ottenere un livello di astrazione più elevato rispetto a C, quelle dichiarazioni extra richieste in C tendono a concentrarsi sull'aggiunta di sicurezza, aggiunta di complessità e gestione degli errori. Quelle sono spesso cose buone, ma hanno un costo , e i suoi nomi sono velocità e dimensioni .

Personalmente, ho scritto letteralmente in dozzine di lingue che coprono la maggior parte dello spettro disponibile e personalmente ho cercato la magia a cui accenni:

Come posso avere la mia torta e anche mangiarla? Come posso giocare con le astrazioni di alto livello nella mia lingua preferita, quindi passare alla nitidezza di C per la velocità?

Dopo un paio d'anni di ricerche, la mia risposta è Python (su C). Potresti dare un'occhiata. A proposito, puoi anche scorrere verso Assembly anche da Python (con qualche piccolo aiuto da una libreria speciale).

D'altra parte, il codice errato può essere scritto in qualsiasi lingua . Pertanto, il codice C (o Assembly) non è automaticamente più veloce. Allo stesso modo, alcuni trucchi per l'ottimizzazione possono portare porzioni di codice linguistico di livello superiore vicino al livello prestazionale di C. crudo. Ma, per la maggior parte delle applicazioni, il programma trascorre la maggior parte del tempo in attesa di persone o hardware, quindi la differenza non ha importanza.

Godere.


10
Questo non si applica davvero ai linguaggi compilati da JIT. Non è come se il mio C # venisse compilato in IL che viene tradotto in C che viene compilato in codice macchina. No, l'IL è compilato da JIT - e il linguaggio di attuazione di JIT è irrilevante a quel punto. Sta solo producendo codice macchina.
Jon Skeet,

3
Dio non voglia che dovrei mettere in discussione il leggendario Jon Skeet, ma sembra del tutto rilevante che il codice macchina in produzione sia per C # anziché C, quindi è "livello superiore", ha più funzionalità, ha controlli di sicurezza, ecc. essere, quindi, più lento dell'equivalente C.
Rob Williams

3
@Jon: Stavo per dire qualcosa di simile, ma il punto è in qualche modo valido perché molti dei componenti principali della libreria .NET sono in realtà scritti in C e quindi hanno i limiti di velocità di C. Sarà interessante vedere come questo cambierà in futuro.
Konrad Rudolph,

1
Questo sembra il modo sbagliato, compilatore / interprete / vms di altre lingue sono spesso ma non sempre scritti in c (o almeno per il livello più basso) perché c è piuttosto veloce (e in molti casi il più veloce).
Roman A. Taycher,

2
Questa risposta non è vera. Come sottolineato in precedenza, non si applica ai linguaggi JIT, ma non si applica anche alle lingue con i propri compilatori (che, se fossero impiegati sforzi straordinari, potrebbero generare codice più veloce dei moderni compilatori C). L'unica altra classe di lingue rimaste sono le lingue interpretate, e quelle non sono più lente di C solo perché sono scritte in C stesse, ma perché il sovraccarico dell'interpretazione, non importa come lo dividi e anche se l'interprete è stato scritto in assemblea , è enorme.
Score_Under

38

Ci sono molte domande lì dentro - principalmente quelle a cui non sono qualificato per rispondere. Ma per quest'ultimo:

cosa impedisce ad altre lingue di essere in grado di compilare in binario che gira veloce quanto C?

In una parola, astrazione.

C dista solo uno o 2 livelli di astrazione dal linguaggio macchina. Java e i linguaggi .Net sono ad almeno 3 livelli di astrazione dall'assemblatore. Non sono sicuro di Python e Ruby.

In genere, più programmatore gioca (tipi di dati complessi, ecc.), Più si viene dal linguaggio automatico e più deve essere eseguita la traduzione.

Vado di qua e di là ma questo è l'essenza di base.

Aggiornamento ------- Ci sono alcuni buoni commenti su questo post con maggiori dettagli.


3
Tecnicamente, Java e .Net sono infinitamente astratti dal linguaggio macchina della macchina su cui girano. Funzionano in VM. Anche con JIT il codice originale deve essere massaggiato in modo massiccio per ottenere qualcosa che assomigli al codice macchina nativo.
jmucchiello,

1
Il codice .net non viene eseguito in una macchina virtuale. Funziona come istruzioni native su qualsiasi piattaforma del processore su cui è in esecuzione (x86 a 32 bit, x86 a 64 bit o IA64).
Robert C. Barth,

11
@ Robert: .net fa utilizzare una macchina virtuale. Il codice .net viene compilato in bytecode che viene eseguito dalla VM. La VM converte il bytecode in istruzioni native in fase di esecuzione.
Robert Gamble,

3
È molto importante notare che Java e altre astrazioni del linguaggio OO hanno influenzato i set di istruzioni del processore. I processori più recenti hanno istruzioni che rendono Java più veloce se la VM Java conosce queste ottimizzazioni e le utilizza. Non è enorme, ma è utile.
Adam Davis,


35

Non è tanto che C è veloce in quanto il modello di costo di C è trasparente . Se un programma C è lento, è lento in modo ovvio: eseguendo molte istruzioni. Rispetto al costo delle operazioni in C, le operazioni di alto livello sugli oggetti (in particolare la riflessione) o le stringhe possono avere costi non ovvi.

Due lingue che generalmente si compilano in binari che sono veloci quanto C sono Standard ML (usando il compilatore MLton ) e Objective Caml . Se dai un'occhiata al gioco dei benchmark , scoprirai che per alcuni benchmark, come gli alberi binari, la versione OCaml è più veloce di C. (Non ho trovato alcuna voce MLton.) Ma non prendere lo shootout troppo sul serio; è, come si suol dire, un gioco, i risultati spesso riflettono quanto sforzo le persone hanno fatto nel mettere a punto il codice.


È possibile scrivere un codice non ovviamente costoso in qualsiasi lingua. È solo che in alcune lingue, devi prima scrivere una variante interiore di Lisp o Forth ...
Donal Fellows

Anche Rust corrisponde a C nei benchmark.
rigido

18

C non è sempre più veloce.

C è più lento di, ad esempio, Modern Fortran.

C è spesso più lento di Java per alcune cose. (Soprattutto dopo che il compilatore JIT ha provato il tuo codice)

C consente che si verifichi un alias puntatore, il che significa che alcune buone ottimizzazioni non sono possibili. Soprattutto quando si hanno più unità di esecuzione, ciò provoca blocchi di recupero dati. Ow.

Il presupposto che l'aritmetica del puntatore funzioni davvero causa prestazioni lenta e gonfie su alcune famiglie di CPU (in particolare PIC!).

Fondamentalmente, quando ottieni un'unità vettoriale o un compilatore in parallelo, C puzza e Fortran moderno funziona più velocemente.

Trucchi del programmatore C come il thunking (modifica al volo dell'eseguibile) causano blocchi di precaricamento della CPU.

Hai la deriva?

E il nostro buon amico, l'x86, esegue un set di istruzioni che oggigiorno ha poca relazione con l'architettura della CPU attuale. Registri ombra, ottimizzatori di caricamento del carico, tutto nella CPU. Quindi C è vicino al metallo virtuale. Il vero metal, Intel non ti fa vedere. (Storicamente le CPU VLIW erano un po 'un busto, quindi, forse non è poi così male.)

Se si programma in C su un DSP ad alte prestazioni (forse un DSP TI?), Il compilatore deve fare alcune cose complicate per srotolare il C tra le diverse unità di esecuzione parallele. Quindi in quel caso C non è vicino al metal, ma è vicino al compilatore, che farà l'ottimizzazione dell'intero programma. Strano.

Infine, alcune CPU (www.ajile.com) eseguono i bytecode Java nell'hardware. C sarebbe un PITA da usare su quella CPU.


1
Quando è stata scritta l'ultima volta il thunking, in C? Il moderno x86 è un'interfaccia per un design per lo più RISC, ma ha poco a che fare con VLIW ...
Calyth

7
Gran parte del tuo post ignora l'esistenza di C99. Inoltre, molti compilatori C / C ++ offrono la parola chiave restringente C99 (garantisce l'assenza di alias puntatore) come estensione.
Evan Teran,

Suppongo che tutti stiano seguendo / passando a seguire la top 25 di CWE / SANS ed evitando di creare nuovi progetti in C. Quindi niente campi verdi C, così poco o niente C99.
Tim Williscroft,

2
Potresti mostrare un esempio quando c è più lento del moderno Fortenberry?
Adam,

Non sono sicuro che ci sia mai stata un'epoca in cui i compilatori C competessero molto bene con i migliori compilatori Fortran. Ovviamente c'è un sacco di codice che non vorresti scrivere in FORTRAN 77 (figuriamoci 66), ma gli standard Fortran più recenti sono stati sempre più piacevoli.
martedì

11

cosa impedisce ad altre lingue di essere in grado di compilare in binario che gira veloce quanto C?

Niente. I linguaggi moderni come i linguaggi Java o .NET sono più orientati alla produttività del programmatore piuttosto che alle prestazioni. L'hardware costa poco oggi. Anche la compilazione per la rappresentazione intermedia offre molti bonus come sicurezza, portabilità, ecc. .NET CLR può sfruttare diversi hardware, ad esempio non è necessario ottimizzare / ricompilare manualmente il programma per utilizzare il set di istruzioni SSE.


direi qui la portabilità. Se vuoi un codice davvero portatile, lo scriverai in C e non in nessun'altra lingua. Abbiamo un codice in esecuzione su circa 25 sistemi operativi. A partire da dos e threadX e finendo su Linux / XP mostrami un'altra lingua che può farlo :)
Ilya

1
@Ilya Non sono d'accordo. È altrettanto facile scrivere codice non portatile in C. Guarda quanto è stato doloroso per alcuni portarlo a 64-bit. I linguaggi di bytecode potrebbero funzionare su tutta la piattaforma se hai l'interprete di bytecode giusto.
Calyth,

1
@IIya, il codice C portatile è un'eccezione piuttosto che una regola, ho portato il codice C tra piattaforme hardware / software diverse e so che è un incubo.
Aku

Anche per PC Word non è così. Guarda nella realtà la maggior parte delle applicazioni multipiattaforma scritte in c / c ++, un po 'in Java. Per lo sviluppo embedded di basso livello non ci sono altri casi. C è di fatto il linguaggio più portatile.
Ilya,

@aku -> PORTING codice errato potrebbe essere un disastro, sono d'accordo. Scrivere codice portatile in ADVANCE - C è la scelta migliore. Direi che C ++ è un'opzione, ma andando su piattaforma integrata troverai sempre un compilatore C decente, per c ++ potresti trovare te stesso senza compilatore.
Ilya,

8

I principali fattori sono che si tratta di un linguaggio tipicamente statico e compilato in codice macchina. Inoltre, poiché è un linguaggio di basso livello, generalmente non fa nulla a cui non lo dici.

Questi sono alcuni altri fattori che vengono in mente.

  • Le variabili non vengono inizializzate automaticamente
  • Nessun limite di controllo sugli array
  • Manipolazione puntatore non selezionata
  • Nessun controllo di overflow di numeri interi
  • Variabili di tipo statico
  • Le chiamate di funzione sono statiche (a meno che non si utilizzino i puntatori di funzione)
  • Gli autori di compilatori hanno avuto molto tempo per migliorare il codice di ottimizzazione. Inoltre, le persone programmano in C allo scopo di ottenere le migliori prestazioni, quindi c'è la pressione per ottimizzare il codice.
  • Parti delle specifiche del linguaggio sono definite dall'implementazione, quindi i compilatori sono liberi di fare le cose nel modo più ottimale

La maggior parte dei linguaggi di tipo statico potrebbero essere compilati altrettanto velocemente o più velocemente di C, soprattutto se possono fare ipotesi che C non può a causa dell'aliasing del puntatore, ecc.


C di basso livello? Immagino che sia un significato relativo ora, rispetto a Java sì ma rispetto all'assembly no. Buon post, mi ha fatto pensare.
Mark

Hai ragione, è decisamente relativo. Voglio dire che è "vicino alla macchina" e non ti aiuta a fare cose come la gestione della memoria o tenere traccia delle dimensioni dell'array.
Matthew Crumley,

2
C è un linguaggio di basso livello. C è sempre stata una lingua di basso livello. Traducete a mano il codice C nell'assemblatore senza troppe difficoltà.
Robert C. Barth,

2
@Robert: C era considerato un linguaggio di alto livello perché rispetto all'assemblaggio (che era molto comune), lo era. È considerata una lingua di basso livello rispetto alla maggior parte delle lingue in uso oggi.
Robert Gamble,

Onestamente, questa è una risposta molto parziale. Accidenti a tutti i programmatori C fanno il controllo dei limiti, ecc. Eppure C è ancora MOLTO più veloce di C ++.
MarcusJ,

8

Immagino che hai dimenticato che anche la lingua dell'Assemblea è una lingua :)

Ma seriamente, i programmi C sono più veloci solo quando il programmatore sa cosa sta facendo. È possibile scrivere facilmente un programma C che viene eseguito più lentamente rispetto ai programmi scritti in altre lingue che svolgono lo stesso lavoro.

Il motivo per cui C è più veloce è perché è progettato in questo modo. Ti consente di fare molte cose di "livello inferiore" che aiutano il compilatore a ottimizzare il codice. Oppure, dovremmo dire, il programmatore è responsabile dell'ottimizzazione del codice. Ma è spesso piuttosto complicato e soggetto a errori.

Altre lingue, come altre già menzionate, si concentrano maggiormente sulla produttività del programmatore. Si ritiene comunemente che il tempo del programmatore sia molto più costoso del tempo della macchina (anche ai vecchi tempi). Quindi ha molto senso ridurre al minimo il tempo che i programmatori impiegano per scrivere e eseguire il debug dei programmi anziché il tempo di esecuzione dei programmi. Per fare ciò, sacrificherai un po 'ciò che puoi fare per rendere il programma più veloce perché molte cose sono automatizzate.


3
Sebbene se scrivessi un programma una volta in C e di nuovo in Assembly, la versione C sarebbe probabilmente più veloce perché il compilatore è più intelligente di te.
mk12

7

Per la maggior parte, ogni istruzione C corrisponde a pochissime istruzioni assembler. Stai essenzialmente scrivendo un codice macchina di livello superiore, quindi hai il controllo su quasi tutto ciò che fa il processore. Molti altri linguaggi compilati, come C ++, hanno molte semplici istruzioni che possono trasformarsi in molto più codice di quanto pensi (funzioni virtuali, costruttori di copie, ecc.) E i linguaggi interpretati come Java o Ruby hanno un altro livello di istruzioni che non vedi mai: la macchina virtuale o l'interprete.


E alcune di queste lingue di alto livello si vantano di essere in grado di rimuovere la maggior parte dell'innesto che hanno aggiunto in primo luogo. Cose come la copia elisione, l'ottimizzazione del valore di ritorno, lo spostamento di costruzione / assegnazione, ecc. In C ++
cmaster - ripristina monica il

Quindi tutto dipende dal numero di istruzioni di assemblaggio generate dal codice. Ci sono più istruzioni di assemblaggio per riga di codice di alto livello, più le prestazioni subiscono?
ENDEESA

Potrebbe essere una semplificazione eccessiva, ma esiste una relazione diretta tra il numero di istruzioni di assemblaggio e la velocità del programma. C'è un tempo minimo per l'esecuzione da parte del processore di ciascuna istruzione. IMHO, una lingua che ti consente di capire facilmente quante istruzioni stai scrivendo ti aiuta a scrivere codice più efficiente. (Ci sono altri costi di tempo del processore, come la mancanza di ramificazioni e cache, ma anche lì, una minore astrazione aiuta a chiarire cosa sta facendo la CPU).
AShelly,


7

Il C ++ è in media più veloce (com'era inizialmente, in gran parte un superset di C, anche se ci sono alcune differenze). Tuttavia, per benchmark specifici, esiste spesso un'altra lingua che è più veloce.

https://benchmarksgame-team.pages.debian.net/benchmarksgame/

fannjuch-redux è stato il più veloce a Scala

n-bodyed fastaerano più veloci in Ada.

spectral-norm è stato il più veloce a Fortran.

reverse-complement, mandelbrotE pidigitssono stati più veloci in ATS.

regex-dna è stato il più veloce in JavaScript.

chameneou-redux è stato il più veloce è Java 7.

thread-ring è stato il più veloce ad Haskell.

Il resto dei benchmark sono stati i più veloci in C o C ++.


"dato che è un super set di C" - No, C ++ non è un superset di C.
PP

1
extern "C"non ha nulla a che fare con il C ++ che è un superset di C.
PP

2
È come dire che system("bash script.sh");funziona per qualsiasi script bash, e quindi C è un superset di bash. extern "C"fornisce il collegamento C in C ++ a causa della modifica del nome. Considerando che chiamare X come un superset di Y significa che tutto ciò che può essere fatto in Y può essere fatto anche in X, il che non è vero per C ++. Esistono alcuni costrutti di linguaggio che sono validi in C ma non in C ++.
PP,

1
@PP Non esiste nulla nello standard C ++ che imponga che bashsia disponibile un programma da riga di comando. Se lo facesse e includesse quale versione / specifica di bash necessitasse di essere supportata, la considererei superset.
Peter Lawrey,

1
è banalmente facile scrivere codice C che non sia codice C ++, ad esempiostruct foo { int: this; }; typedef float foo;
Jasen,

6

Molte di queste risposte forniscono validi motivi per cui C è o non è più veloce (in generale o in scenari specifici). È innegabile che:

  • Molte altre lingue forniscono funzionalità automatiche che diamo per scontate. Il controllo dei limiti, il controllo del tipo di runtime e la gestione automatica della memoria, ad esempio, non sono gratuiti. Ce ne sono almeno alcuni costi associati a queste funzionalità, alle quali potremmo non pensare, o addirittura realizzare, durante la scrittura di codice che utilizza queste funzionalità.
  • Il passaggio dall'origine alla macchina spesso non è diretto in altre lingue come in C.
  • OTOH, dire che il codice C compilato viene eseguito più velocemente di altri codici scritti in altre lingue è una generalizzazione che non è sempre vera. I contro-esempi sono facili da trovare (o escogitare).

Ciò nonostante, c'è qualcos'altro che ho notato che, penso, influenza le prestazioni comparative della C rispetto a molte altre lingue più di qualsiasi altro fattore. Per dire:

Altre lingue spesso semplificano la scrittura di codice che viene eseguito più lentamente. Spesso è persino incoraggiato dalle filosofie progettuali del linguaggio. Corollario: un programmatore C ha maggiori probabilità di scrivere codice che non esegue operazioni non necessarie.

Ad esempio, considera un semplice programma Windows in cui viene creata una singola finestra principale. La versione AC popolerebbe una WNDCLASS[EX]struttura a cui verrebbe passato RegisterClass[Ex], quindi chiamerebbe CreateWindow[Ex]ed entrava in un ciclo di messaggi. Segue un codice altamente semplificato e abbreviato:

WNDCLASS wc;
MSG      msg;

wc.style         = 0;
wc.lpfnWndProc   = &WndProc;
wc.cbClsExtra    = 0;
wc.cbWndExtra    = 0;
wc.hInstance     = hInstance;
wc.hIcon         = NULL;
wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.lpszMenuName  = NULL;
wc.lpszClassName = "MainWndCls";

RegisterClass(&wc);

CreateWindow("MainWndCls", "", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
             CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

while(GetMessage(&msg, NULL, 0, 0)){
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

Un programma equivalente in C # potrebbe essere solo una riga di codice:

Application.Run(new Form());

Questa riga di codice fornisce tutte le funzionalità di quasi 20 righe di codice C e aggiunge alcune cose che abbiamo lasciato fuori, come il controllo degli errori. La libreria più ricca e più completa (rispetto a quelle utilizzate in un tipico progetto C) ha fatto molto lavoro per noi, liberando il nostro tempo per scrivere molti più frammenti di codice che ci sembrano brevi ma comportano molti passaggi dietro le quinte.

Ma una ricca libreria che consente di gonfiare il codice in modo semplice e rapido non è proprio il mio punto di vista. Il mio punto è più evidente quando inizi a esaminare cosa succede effettivamente quando il nostro piccolo one-liner esegue effettivamente. Per un po 'di divertimento, abilita l'accesso ai sorgenti .NET in Visual Studio 2008 o versioni successive e passa alla semplice riga precedente sopra. Una delle piccole gemme divertenti che incontrerai è questo commento nel getter per Control.CreateParams:

// In a typical control this is accessed ten times to create and show a control.
// It is a net memory savings, then, to maintain a copy on control.
// 
if (createParams == null) {
    createParams = new CreateParams(); 
} 

Dieci volte . Le informazioni all'incirca equivalenti alla somma di ciò che è archiviato in una WNDCLASSEXstruttura e ciò a cui CreateWindowExviene passato viene recuperato dalla Controlclasse dieci volte prima di essere archiviato in una WNDCLASSEXstruttura e trasmesso a RegisterClassExeCreateWindowEx .

Tutto sommato, il numero di istruzioni eseguite per eseguire questo compito di base è di 2-3 ordini di grandezza in più in C # che in C. Parte di ciò è dovuta all'uso di una libreria ricca di funzionalità, che è necessariamente generalizzata, contro il nostro semplice codice C che fa esattamente ciò di cui abbiamo bisogno e niente di più. Ma in parte è dovuto al fatto che la natura modulare e orientata agli oggetti del framework .NET si presta a molte ripetizioni di esecuzione che spesso vengono evitate da un approccio procedurale.

Non sto cercando di scegliere C # o .NET Framework. Né sto dicendo che la modularizzazione, la generalizzazione, le funzionalità di libreria / linguaggio, OOP, ecc. Siano cose cattive . Ero solito fare la maggior parte del mio sviluppo in C, in seguito in C ++ e ultimamente in C #. Allo stesso modo, prima di C, ho usato principalmente il montaggio. E ad ogni passo "più alto" la mia lingua va, scrivo programmi migliori, più gestibili, più robusti in meno tempo. Tendono, tuttavia, a eseguire un po 'più lentamente.


2
Questo è un problema API, non un problema di lingua.
Arafangion,

1
@Arafangion: capisco quello che stai dicendo, ma in qualche modo manca il punto. La libreria ricca di funzionalità è abilitata (e, in un certo senso, richiesta) dal linguaggio ricco di funzionalità. E non è solo la biblioteca. La libreria è solo un esempio di uso comune della lingua. Il tipico codice di applicazione in qualsiasi lingua ha generalmente una somiglianza con le librerie tipicamente utilizzate in quella lingua. È davvero più una mentalità favorita dalla lingua. Ad esempio, le lingue OO generalmente impiegano più tempo ad allocare, costruire, distruggere e deallocare oggetti rispetto alle lingue con meno supporto OOP.
P Daddy,

Concedo che una determinata scelta di linguaggio implica generalmente una particolare piattaforma e libreria, motivo per cui ho fatto quel commento (in modo che il lettore fosse più consapevole), ma detto questo, usando (ad esempio) C ++ su Windows è un bestia molto diversa, diciamo, C ++ su Linux e di nuovo diversa con C ++ su Android. Un altro esempio è Python - abbiamo CPython, Jython, PyPy e IronPython - che usano tutte librerie molto diverse.
Arafangion,

Ma usando uno di questi Python, gli sviluppatori tenderanno a scrivere applicazioni in un certo modo. Ad esempio, possono leggere e scrivere da un file di testo, creando nuovi oggetti con i dati letti. In C, d'altra parte, uno sviluppatore farebbe più probabilmente un'allocazione una tantum di una matrice di strutture e leggerà e scriverà quelle strutture da un file binario. Questo, ovviamente, è semplicemente un esempio inventato che tenta di illustrare il punto che sto cercando di fare riguardo alla mentalità .
P Daddy,

6

Non credo che nessuno abbia menzionato il fatto che sono stati fatti molti più sforzi nei compilatori C rispetto a qualsiasi altro compilatore, con l'eccezione di Java.

C è estremamente ottimizzabile per molte delle ragioni già indicate, più di quasi qualsiasi altra lingua. Quindi, se la stessa quantità di sforzo viene impiegata in altri compilatori di lingue, probabilmente C continuerà ad essere in cima.

Penso che ci sia almeno un linguaggio candidato che con sforzo potrebbe essere ottimizzato meglio di C e quindi potremmo vedere implementazioni che producono file binari più veloci. Sto pensando a digital mars D perché il creatore si è preso cura di costruire un linguaggio che potrebbe essere potenzialmente ottimizzato meglio di C. Potrebbero esserci altre lingue che hanno questa possibilità. Tuttavia, non riesco a immaginare che nessun linguaggio avrà compilatori più di qualche percento più veloci dei migliori compilatori C. Mi piacerebbe sbagliarmi.

Penso che il vero "frutto basso appeso" sarà nelle lingue che sono progettate per essere FACILE da ottimizzare per gli esseri umani. Un programmatore esperto può rendere più veloce qualsiasi lingua, ma a volte devi fare cose ridicole o usare costrutti innaturali per farlo accadere. Sebbene ci vorrà sempre uno sforzo, un buon linguaggio dovrebbe produrre un codice relativamente veloce senza dover ossessionare esattamente come è scritto il programma.

È anche importante (almeno per me) che il codice del caso peggiore tenda ad essere veloce. Esistono numerose "prove" sul web che Java è più veloce o più veloce di C, ma che si basa su esempi di raccolta delle ciliegie. Non sono un grande fan di C, ma so che QUALUNQUE cosa scrivo in C funzionerà bene. Con Java funzionerà "probabilmente" entro il 15% della velocità, di solito entro il 25% ma in alcuni casi può essere molto peggio. Tutti i casi in cui è altrettanto veloce o entro un paio di percento sono generalmente dovuti alla maggior parte del tempo trascorso nel codice della libreria che è fortemente ottimizzato C comunque.


5

Questo è in realtà un po 'una menzogna perpetuata. Mentre è vero che i programmi C sono spesso più veloci, non è sempre così, specialmente se il programmatore C non è molto bravo.

Un grande buco evidente che le persone tendono a dimenticare è quando il programma deve bloccare una sorta di IO, come l'input dell'utente in qualsiasi programma della GUI. In questi casi, non importa quale lingua utilizzi poiché sei limitato dalla velocità con cui i dati possono arrivare piuttosto che dalla velocità con cui puoi elaborarli. In questo caso, non importa molto se stai usando C, Java, C # o persino Perl; non puoi andare più veloce di quanto possano entrare i dati.

L'altra cosa importante è che l'uso della garbage collection e non l'utilizzo di puntatori adeguati consente alla macchina virtuale di rendere non disponibili altre ottimizzazioni in altre lingue. Ad esempio, JVM è in grado di spostare oggetti sull'heap per deframmentarlo. Ciò rende le allocazioni future molto più veloci poiché l'indice successivo può essere semplicemente utilizzato anziché cercarlo in una tabella. Inoltre, le moderne JVM non devono realmente dislocare la memoria; invece, spostano semplicemente gli oggetti dal vivo quando GC e la memoria esaurita dagli oggetti morti viene recuperata essenzialmente gratuitamente.

Questo porta anche un punto interessante su C e ancora di più in C ++. C'è una sorta di filosofia del design di "Se non ne hai bisogno, non paghi per questo". Il problema è che se lo vuoi, finisci per pagare per il naso. Ad esempio, l'implementazione della vtable in Java tende ad essere molto migliore delle implementazioni C ++, quindi le chiamate di funzione virtuali sono molto più veloci. D'altra parte, non hai altra scelta che utilizzare funzioni virtuali in Java e costano ancora qualcosa, ma in programmi che usano molte funzioni virtuali, il costo ridotto si somma.


1
"l'implementazione della vtable in Java tende ad essere molto migliore delle implementazioni in C ++, quindi le chiamate di funzioni virtuali sono molto più veloci.". Come mai puoi andare più veloce di MOV EAX, [ECX]; CALL [EAX + someindex]; ? A meno che non sia possibile chiamare una funzione senza cercarla, questo sembra piuttosto ottimale.
Frans-Willem,

@Frans: un compilatore JIT (come Java HotSpot) può incorporare la ricerca vtable se determina che un determinato oggetto è sempre di un determinato tipo. C ++ lo farà anche se conosce le stesse informazioni in fase di compilazione, ma è più semplice eseguire questa ottimizzazione con bytecode Java che con le istruzioni macchina x86.
Tom,

6
@James - La discussione "L'I / O rende le prestazioni meno importanti" non invalida l'affermazione "C è più veloce di altre lingue". Non è un buco lampante, è un argomento da pagliaccio.
Tom,

Sarebbe stato meglio usare la gestione delle stringhe di C (e anche la libreria C standard) come esempio, in quanto questa è un'area in cui C è scarsa. Molte altre lingue funzionano meglio, anche con un codice di partenza semplicistico.
Donal Fellows,

@DonalFellows le funzioni mem * possono essere più veloci delle funzioni str * in alcune attività, ma la gestione delle stringhe è efficiente se si presta attenzione. hai in mente un benchmark specifico?
Jasen,

4

Non si tratta tanto del linguaggio quanto degli strumenti e delle librerie. Le librerie e i compilatori disponibili per C sono molto più vecchi di quelli per le lingue più recenti. Potresti pensare che questo li renderebbe più lenti, ma al contrario.

Queste librerie sono state scritte in un momento in cui la potenza di elaborazione e la memoria erano un premio. Hanno dovuto essere scritti in modo molto efficiente per funzionare affatto. Gli sviluppatori di compilatori C hanno anche avuto molto tempo per lavorare su tutti i tipi di ottimizzazioni intelligenti per diversi processori. La maturità e l'ampia adozione di C offrono un vantaggio significativo rispetto ad altre lingue della stessa età. Offre inoltre a C un vantaggio in termini di velocità rispetto a strumenti più recenti che non enfatizzano le prestazioni grezze tanto quanto C doveva.


4

La mancanza di astrazione è ciò che rende C più veloce. Se scrivi un'istruzione di output sai esattamente cosa sta succedendo. Se scrivi un'istruzione di output in Java, viene compilata in un file di classe che viene quindi eseguito su una macchina virtuale introducendo un livello di astrazione. La mancanza di funzionalità orientate agli oggetti come parte del linguaggio aumenta anche la sua velocità rispetto a meno codice generato. Se usi C come linguaggio orientato agli oggetti, stai facendo tutto il codice per cose come classi, inharitence, ecc. Ciò significa piuttosto fare qualcosa di abbastanza generalizzato per tutti con la quantità di codice e la penalità delle prestazioni che richiede solo di scrivere quello che ti serve per fare il lavoro.


4

Incredibile vedere il vecchio "C / C ++ deve essere più veloce di Java perché Java è interpretato" il mito è ancora vivo e vegeto. Ci sono articoli che vanno indietro di alcuni anni , oltre a quelli più recenti , che spiegano con concetti o misurazioni perché semplicemente non è sempre così .

Le attuali implementazioni di macchine virtuali (e non solo la JVM, tra l'altro) possono sfruttare le informazioni raccolte durante l'esecuzione del programma per ottimizzare dinamicamente il codice durante l'esecuzione, utilizzando una varietà di tecniche:

  • rendere frequenti i metodi al codice macchina,
  • allineando piccoli metodi,
  • regolazione del bloccaggio

e una varietà di altre regolazioni basate sulla conoscenza di ciò che il codice sta effettivamente facendo e sulle caratteristiche effettive dell'ambiente in cui è in esecuzione.


Concordo sul fatto che negli ultimi anni Java abbia apportato significativi miglioramenti delle prestazioni che lo avvicinano molto di più a C in termini di prestazioni non elaborate, ma ci vorrà del tempo per ridurre il fatto che è stato così lento per così tanto tempo. Ma chi parlava comunque di Java?
Robert Gamble,

Java è un "altro linguaggio" a cui fa riferimento l'OP, non è vero?
Robert C. Barth,

@Robert: "altre lingue", plurale, nessuna menzione di una lingua specifica diversa da C. Come si può forse leggere "Java" da quello?
Robert Gamble,

@Roberd: Molte delle risposte che erano state pubblicate prima che io incontrassi la domanda parlavano di Java (o di altre lingue la cui implementazione è spesso tramite interprete o VM).
joel.neely

3
@Joel - Se conosci l'hardware di destinazione, la maggior parte delle ottimizzazioni che JVM può eseguire in fase di esecuzione possono anche essere eseguite utilizzando l'ottimizzazione guidata dal profilo con C o C ++. Ciò fa una differenza enorme , e generalmente spinge C e C ++ in testa, poiché non devono "imparare" durante l'esecuzione.
Tom,

4

Il codice più veloce sarebbe il codice macchina accuratamente realizzato a mano. L'assemblatore sarà quasi altrettanto buono. Entrambi sono di livello molto basso e ci vuole molto codice per scrivere. C è un po 'sopra l'assemblatore. Hai ancora la possibilità di controllare le cose a un livello molto basso nella macchina reale, ma c'è abbastanza astrazione che rende la scrittura più veloce e più facile dell'assemblatore. Altre lingue come C # e JAVA sono ancora più astratte. Mentre assemblatore e codice macchina sono chiamati linguaggi di basso livello, C # e JAVA (e molti altri) sono chiamati linguaggi di alto livello. C è talvolta chiamato una lingua di livello medio.


Mentre leggevo la tua risposta, solo due parole in tutto il paragrafo mi stavano attirando gli occhi, come un magnete che tira i metalli. La parola è JAVA, due volte nel paragrafo. Non l'ho mai visto prima scritto in maiuscolo e sembra buono :-)
Sнаđошƒаӽ

3

Non dare per scontato qualcuno, guarda il disassemblaggio sia per C che per la tua lingua preferita in qualsiasi parte critica del rendimento del tuo codice. Penso che puoi semplicemente guardare nella finestra di smontaggio in fase di esecuzione in Visual Studio per vedere .Net smontato. Dovrebbe essere possibile se complicato per Java usando windbg, anche se lo fai con .Net molti dei problemi sarebbero gli stessi.

Non mi piace scrivere in C se non ne ho bisogno, ma penso che molte delle affermazioni fatte in queste risposte che sostengono la velocità di lingue diverse da C possano essere messe da parte semplicemente smontando la stessa routine in C e nella tua lingua di livello superiore, specialmente se sono coinvolti molti dati, come è comune nelle applicazioni critiche per le prestazioni. Fortran potrebbe essere un'eccezione nella sua area di competenza, non lo so. È di livello superiore a C?

La prima volta che ho confrontato il codice JITed con il codice nativo ho risolto tutte le domande sul fatto che il codice .Net potesse essere comparato al codice C. Il livello extra di astrazione e tutti i controlli di sicurezza comportano un costo significativo. Gli stessi costi verrebbero probabilmente applicati a Java, ma non crederci, provalo su qualcosa in cui le prestazioni sono fondamentali. (Qualcuno sa abbastanza su JITed Java per individuare una procedura compilata in memoria? Dovrebbe certamente essere possibile)


2

1) Come altri hanno già detto, C fa di meno per te. Nessuna variabile di inizializzazione, nessun controllo dei limiti dell'array, nessuna gestione della memoria, ecc. Quelle funzionalità in altre lingue costano i cicli di memoria e CPU che C non spende.

2) Le risposte dicendo che C è meno astratto e quindi più veloce sono solo metà corrette, penso. Tecnicamente parlando, se avessi un "compilatore sufficientemente avanzato" per il linguaggio X, allora il linguaggio X potrebbe avvicinarsi o eguagliare la velocità di C. La differenza con C è che dal momento che mappa così ovviamente (se hai seguito un corso di architettura) e direttamente al linguaggio assembly che anche un compilatore ingenuo può fare un lavoro decente. Per qualcosa come Python, è necessario un compilatore molto avanzato per prevedere i probabili tipi di oggetti e generare al volo il codice macchina: la semantica di C è abbastanza semplice da consentire a un semplice compilatore di funzionare bene.


2

In passato, c'erano solo due tipi di lingue: compilate e interpretate.

I linguaggi compilati hanno utilizzato un "compilatore" per leggere la sintassi del linguaggio e convertirlo in un codice identico per il linguaggio assembly, che potrebbe non solo direttamente sulla CPU. Le lingue interpretate utilizzavano un paio di schemi diversi, ma essenzialmente la sintassi del linguaggio veniva convertita in una forma intermedia e quindi eseguita in un "interprete", un ambiente per eseguire il codice.

Quindi, in un certo senso, c'era un altro "livello" - l'interprete - tra il codice e la macchina. E, come sempre nel caso di un computer, più significa che vengono utilizzate più risorse. Gli interpreti erano più lenti, perché dovevano eseguire più operazioni.

Più recentemente, abbiamo visto più linguaggi ibridi come Java, che impiegano sia un compilatore che un interprete per farli funzionare. È complicato, ma una JVM è più veloce, più sofisticata e molto più ottimizzata rispetto ai vecchi interpreti, quindi presenta un cambiamento molto migliore nell'esecuzione (nel tempo) più vicino al semplice codice compilato. Ovviamente, i compilatori più recenti hanno anche trucchi per l'ottimizzazione più fantasiosi, quindi tendono a generare codice molto migliore di quanto non facessero anche loro. Ma la maggior parte delle ottimizzazioni, il più delle volte (sebbene non sempre) effettuano un qualche tipo di compromesso in modo tale da non essere sempre più veloci in tutte le circostanze. Come tutto il resto, niente viene offerto gratuitamente, quindi gli ottimizzatori devono trarre vantaggio da qualche parte (anche se spesso lo utilizzano utilizzando la CPU in fase di compilazione per salvare la CPU di runtime).

Tornando a C, è un linguaggio semplice, che può essere compilato in assembly abbastanza ottimizzato e quindi eseguito direttamente sulla macchina di destinazione. In C, se si incrementa un numero intero, è più che probabile che sia solo un passo dell'assemblatore nella CPU, in Java, tuttavia, potrebbe finire per essere molto più di questo (e potrebbe includere anche un po 'di garbage collection: -) C ti offre un'astrazione che è molto più vicina alla macchina (l'assemblatore è il più vicino), ma alla fine devi fare molto più lavoro per farlo funzionare e non è così protetto, facile da usare o facile da correggere. La maggior parte delle altre lingue ti dà una maggiore astrazione e si prende cura di più dei dettagli sottostanti per te, ma in cambio della loro funzionalità avanzata richiedono più risorse per funzionare. Man mano che generalizzi alcune soluzioni, devi gestire una gamma più ampia di elaborazione,

Paolo.


"In C, se si incrementa un numero intero, è più che probabile che sia solo un passo dell'assemblatore nella CPU" Non esattamente vero. Se questo numero intero non si trova nel registro della CPU, è necessario disporre del codice macchina per recuperarlo dalla memoria, incrementarlo, riscriverlo in memoria. Quasi lo stesso si aspetterebbe che accada quando si esegue il codice Java. E non ho idea del perché "++ i" possa innescare da solo un ciclo GC.
quant_dev,

@quant_dev: "tu ... devi ... recuperarlo dalla memoria, incrementarlo, riscriverlo ...". Forse sì forse no. L'x86, ad esempio, ha istruzioni che operano sui dati in memoria. ++ipotrebbe compilare per "aggiungere [ebp - 8], 1". Per non dire che il recupero, l'incremento, l'archiviazione non sta ancora avvenendo, ma questo è curato dalla CPU ed è solo un'istruzione, come ha detto Paul.
P Daddy,

... che è ancora irrilevante dal POV delle prestazioni, perché potrebbe essere solo un'istruzione, ma comporta comunque l'attesa che i dati arrivino alla CPU. Manca la cache e tutto il resto.
quant_dev,

No, non direi che è irrilevante. Un'istruzione CPU in genere occupa meno byte di codice rispetto a più istruzioni CPU, offrendo migliori prestazioni della cache sul segmento di codice. Un'istruzione CPU occupa anche meno spazio sulla pipeline della CPU e i passaggi multipli (recupero, incremento, memorizzazione) possono essere gestiti, forse in parallelo, dagli stadi della pipeline separati. In effetti, alcune parti di un'operazione possono anche essere saltate se possono essere combinate con altre operazioni sulla tubazione. Ad esempio, la memorizzazione di un valore può essere combinata con un successivo caricamento dello stesso valore.
P Daddy,

2

Mettendo da parte tecniche di ottimizzazione avanzate come l'ottimizzazione hot-spot , meta-algoritmi precompilati e varie forme di parallelismo , la velocità fondamentale di un linguaggio è fortemente correlata alla complessità implicita dietro le quinte richiesta per supportare le operazioni che comunemente essere specificato nei circuiti interni .

Forse il più ovvio è il controllo di validità sui riferimenti di memoria indiretta, come il controllo dei puntatori nulle il controllo degli indici rispetto ai limiti dell'array. La maggior parte delle lingue di alto livello esegue questi controlli implicitamente, ma C no. Tuttavia, questo non è necessariamente un limite fondamentale di questi altri linguaggi : un compilatore sufficientemente intelligente potrebbe essere in grado di rimuovere questi controlli dai loop interni di un algoritmo attraverso una qualche forma di movimento del codice invariante .

Il vantaggio più fondamentale di C (e in misura simile del C ++ strettamente correlato) è una forte dipendenza dall'allocazione di memoria basata su stack , che è intrinsecamente veloce per allocazione, deallocazione e accesso. In C (e C ++) lo stack di chiamate principale può essere utilizzato per l'allocazione di primitive, array e aggregati ( struct/class ).

Mentre C offre la possibilità di allocare dinamicamente memoria di dimensioni e durata arbitrarie (usando il cosiddetto 'heap'), per impostazione predefinita viene evitato (viene invece utilizzato lo stack).

Tantalizing, a volte è possibile replicare la strategia di allocazione della memoria C all'interno degli ambienti di runtime di altri linguaggi di programmazione. Ciò è stato dimostrato da asm.js , che consente di tradurre il codice scritto in C o C ++ in un sottoinsieme di JavaScript ed eseguirlo in sicurezza in un ambiente browser Web, con una velocità quasi nativa.


A parte questo, un'altra area in cui C e C ++ superano la maggior parte degli altri linguaggi per la velocità è la capacità di integrarsi perfettamente con i set di istruzioni della macchina nativa. Un esempio notevole di ciò è la disponibilità ( intrinseca del compilatore e della piattaforma) di intrinseci SIMD che supportano la costruzione di algoritmi personalizzati che sfruttano l'ormai quasi onnipresente hardware di elaborazione parallela, pur utilizzando le astrazioni di allocazione dei dati fornite dal linguaggio (inferiore l'allocazione del registro di livello è gestita dal compilatore).


1
Alcuni di questi vantaggi nell'allocazione della memoria di C possono probabilmente essere replicati anche in altri linguaggi da compilatori intelligenti (vedi qui ). Ho l'impressione, tuttavia, che questo sia in qualche modo strutturalmente molto difficile da fare, specialmente per i tipi di dati non primitivi. Questo post menziona l'idea di un oggetto senza escape che può essere allocato in pila come ottimizzazione in Java.
nobar,

2

Ho trovato una risposta sul link sul perché alcune lingue sono più veloci e alcune sono più lente, spero che questo chiarisca di più sul perché C o C ++ è più veloce di altri, ci sono anche altre lingue più veloci di C, ma non possiamo usali tutti. Qualche spiegazione -

Uno dei motivi principali per cui Fortran rimane importante è perché è veloce: le routine di scricchiolio dei numeri scritte in Fortran tendono ad essere più veloci delle routine equivalenti scritte nella maggior parte delle altre lingue. I linguaggi che sono in competizione con Fortran in questo spazio - C e C ++ - sono usati perché competitivi con questa performance.

Questo solleva la domanda: perché? Cosa c'è in C ++ e Fortran che li rendono veloci e perché superano altri linguaggi popolari, come Java o Python?

Interpretazione contro compilazione Esistono molti modi per classificare e definire i linguaggi di programmazione, in base allo stile di programmazione che incoraggiano e alle funzionalità che offrono. Quando si guarda alla performance, la più grande distinzione singola è tra i linguaggi interpretati e quelli compilati.

La divisione non è difficile; piuttosto, c'è uno spettro. Ad una estremità, abbiamo linguaggi compilati tradizionali, un gruppo che include Fortran, C e C ++. In queste lingue, esiste una fase di compilazione discreta che traduce il codice sorgente di un programma in una forma eseguibile che il processore può utilizzare.

Questo processo di compilazione prevede diversi passaggi. Il codice sorgente viene analizzato e analizzato. A questo punto è possibile rilevare errori di codifica di base come errori di battitura e errori di ortografia. Il codice analizzato viene utilizzato per generare una rappresentazione in memoria, che può essere utilizzata anche per rilevare errori, questa volta errori semantici, come la chiamata di funzioni che non esistono o il tentativo di eseguire operazioni aritmetiche su stringhe di testo.

Questa rappresentazione in memoria viene quindi utilizzata per guidare un generatore di codice, la parte che produce codice eseguibile. L'ottimizzazione del codice, per migliorare le prestazioni del codice generato, viene eseguita in vari momenti all'interno di questo processo: ottimizzazioni di alto livello possono essere eseguite sulla rappresentazione del codice e ottimizzazioni di livello inferiore vengono utilizzate sull'output del generatore di codice.

L'esecuzione effettiva del codice avviene successivamente. L'intero processo di compilazione viene semplicemente utilizzato per creare qualcosa che può essere eseguito.

All'estremità opposta, abbiamo interpreti. Gli interpreti includeranno una fase di analisi simile a quella del compilatore, ma questo viene quindi utilizzato per guidare l'esecuzione diretta, con il programma che viene eseguito immediatamente.

L'interprete più semplice ha al suo interno un codice eseguibile corrispondente alle varie funzionalità supportate dalla lingua, quindi avrà funzioni per l'aggiunta di numeri, l'unione di stringhe, qualsiasi altra lingua abbia. Mentre analizza il codice, cercherà la funzione corrispondente ed eseguirà. Le variabili create nel programma verranno mantenute in una sorta di tabella di ricerca che associa i loro nomi ai loro dati.

L'esempio più estremo dello stile dell'interprete è qualcosa come un file batch o uno script di shell. In queste lingue, il codice eseguibile spesso non è nemmeno incorporato nell'interprete stesso, ma piuttosto in programmi autonomi separati.

Quindi perché questo fa la differenza per le prestazioni? In generale, ogni livello di riferimento indiretto riduce le prestazioni. Ad esempio, il modo più veloce per aggiungere due numeri è di avere entrambi quei numeri nei registri del processore e di usare le istruzioni di aggiunta del processore. Ecco cosa possono fare i programmi compilati; possono inserire variabili nei registri e trarre vantaggio dalle istruzioni del processore. Ma nei programmi interpretati, quella stessa aggiunta potrebbe richiedere due ricerche in una tabella di variabili per recuperare i valori da aggiungere, quindi chiamare una funzione per eseguire l'aggiunta. Tale funzione può benissimo utilizzare le stesse istruzioni del processore utilizzate dal programma compilato per eseguire l'aggiunta effettiva, ma tutto il lavoro extra prima che l'istruzione possa effettivamente essere utilizzata rende le cose più lente.

Se vuoi saperne di più, controlla la fonte


1

Alcuni algoritmi C ++ sono più veloci di C e alcune implementazioni di algoritmi o modelli di progettazione in altri linguaggi possono essere più veloci di C.

Quando le persone affermano che C è veloce e poi passano a parlare di qualche altra lingua, generalmente usano la performance di C come riferimento.


2
"Alcuni algoritmi C ++ sono più veloci di C" non ha senso. Qualsiasi "algoritmo C ++" può essere scritto in C e viceversa. Gli algoritmi sono agnostici del linguaggio. C ++ essenzialmente aggiunge funzionalità a C - e nessuna di queste nuove funzionalità porta ad algoritmi più veloci (anche se forse sono più facili da scrivere).
Joseph Garvin,

1
Il rimprovero classico è l'algoritmo std :: sort .. std :: sort è più veloce di qualsiasi algoritmo di ordinamento in C - l'unico modo per ottenere le stesse prestazioni in C è codificarlo ovunque tu voglia o usare le macro - e anche allora il compilatore ha meno informazioni da ottimizzare.
Arafangion,

1

Con i moderni compilatori di ottimizzazione, è altamente improbabile che un programma C puro sia molto più veloce del codice .net compilato, se non del tutto. Con il miglioramento della produttività che framework come .net forniscono allo sviluppatore, puoi fare cose in un giorno che impiegava settimane o mesi nella normale C. Insieme al costo economico dell'hardware rispetto allo stipendio di uno sviluppatore, è semplicemente MODO più economico scrivere il materiale in un linguaggio di alto livello e getta l'hardware a qualsiasi lentezza.

Il motivo per cui Jeff e Joel parlano del fatto che C è il linguaggio "programmatore reale" è perché non c'è tenuta di mano in C. Devi allocare la tua memoria, deallocare quella memoria, fare il tuo controllo dei limiti, ecc. Non esiste nulla del genere come nuovo oggetto (); Non ci sono garbage collection, classi, OOP, framework di entità, LINQ, proprietà, attributi, campi o simili. Devi sapere cose come l'aritmetica del puntatore e come dereferenziare un puntatore. E, del resto, conosci e capisci cos'è un puntatore. Devi sapere cos'è uno stack frame e qual è il puntatore alle istruzioni. Devi conoscere il modello di memoria dell'architettura CPU su cui stai lavorando. C'è molta comprensione implicita dell'architettura di un microcomputer (di solito ilmicrocomputer su cui stai lavorando) durante la programmazione in C che semplicemente non è presente né necessaria quando si programma in qualcosa come C # o Java. Tutte queste informazioni sono state scaricate nel programmatore del compilatore (o VM).


"Mostra più hardware al problema" funziona solo in ambienti dove ciò è effettivamente possibile. Il mercato incorporato è un perfetto contro-esempio (e questo è un mercato enorme ).
Bob Somers,

Il blog di Jeff e Joel riguarda esclusivamente i sistemi aziendali, non i sistemi incorporati, quindi è ragionevole supporre che quello sia il contesto in cui è stata posta questa domanda.
Robert C. Barth,

1) codice .net in esecuzione veloce come il codice C? Hai mai davvero scritto un programma C? 2) La mentalità "gettare più hardware al problema" è il motivo per cui la mia macchina dual core da 1,3 GHz da 2 GB riesce a malapena a tenere il passo con Windows XP mentre la mia macchina da 800 MHz da 512 MB vola con l'ultima versione di Ubuntu.
Robert Gamble,

Sì, ho scritto C. Non è glorioso come la gente capisce di essere. E i progetti costano troppo. È un semplice caso di economia. Ho usato Win2k su un Pentium Pro 180MHz con 768 MB di RAM per anni come mail e web server. Funzionava bene. Le prove aneddotiche non significano nulla.
Robert C. Barth,

C non è "glorioso" ma è veloce, ho scritto abbastanza codice C e C # per sapere che C quasi sempre molto più velocemente di C # mentre eseguo la stessa attività. Per alcune attività lo sviluppo richiede più tempo rispetto ai linguaggi di livello superiore, ma si tratta solo di utilizzare lo strumento giusto per il lavoro e talvolta C è.
Robert Gamble,

1

È la differenza tra automatico e manuale, le lingue di livello superiore sono le astrazioni così automatizzate. C / C ++ sono controllati e gestiti manualmente, anche il codice di controllo degli errori a volte è un lavoro manuale.

C e C ++ sono anche linguaggi compilati, il che significa che nessuno di questi funziona ovunque, queste lingue devono essere ottimizzate per l'hardware con cui lavori, aggiungendo così un ulteriore livello di gotcha. Anche se questo è un po 'inquietante ora come i compilatori C / C ++ stanno diventando più comuni su tutte le piattaforme. È possibile eseguire compilazioni incrociate tra piattaforme. Non è ancora una situazione dappertutto, il tuo fondamentalmente istruisci il compilatore A di compilare contro lo stesso codice B con architettura diversa.

I linguaggi della linea di fondo C non sono pensati per essere facili da capire o ragionare, anche per questo vengono chiamati linguaggi di sistema. Sono usciti prima di tutte queste assurdità di astrazione di alto livello. Questo è anche il motivo per cui non vengono utilizzati per la programmazione Web front-end. Non sono adatti al compito, ma sono intesi a risolvere problemi complessi che non possono essere risolti con strumenti linguistici convenzionali.

Questo è il motivo per cui ottieni cose folli come (micro-architetture, driver, fisica quantistica, giochi AAA, sistemi operativi) ci sono cose per cui C e C ++ sono adatte. La velocità e lo scricchiolio dei numeri sono le aree principali.



1

Ci sono molte ragioni, tra cui:

  • Si compila in linguaggio assembly.
  • È staticamente tipizzato.
  • Nessuna raccolta rifiuti.
  • Nessuna gestione degli errori.
  • Molti linguaggi di programmazione aggiungono nuove funzionalità. Parte della filosofia di C è quella di mantenere le cose semplici invece di aggiungere più funzionalità.

Perché dovrebbe essere più veloce perché non è orientato agli oggetti?
sb27

A) la programmazione orientata agli oggetti crea un'esigenza di garbage collection, B) grandi funzionalità come la programmazione orientata agli oggetti aggiungono complessità al compilatore,
Sapphire_Brick

A) No. Vedi C ++ o Rust. B) Sì, ma offre anche al compilatore opportunità di nuove ottimizzazioni.
sb27,

A) Rust ha una garbage collection in fase di compilazione e c ++ ha garbage collection per le classi, ecco perché ha distruttori, B) la complessità ottimizzata è ancora complessità
Sapphire_Brick

A) Non è garbage collection, la gestione della memoria deve essere eseguita anche se si esegue il programma nell'assembly B) No. Più astrazione consente all'ottimizzatore di formulare ipotesi migliori.
sb27
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.