Perché Java è un fattore 2-3 più lento di un equivalente programma C ++? [chiuso]

0

So che esiste un'opinione secondo cui i programmi scritti in Java e in esecuzione in JVM sono veloci quanto i programmi C ++, dopo aver introdotto just-in-time (JIT). Vedo molti casi in cui Java è terribilmente lento per programmi semplici rispetto a C ++, vale a dire circa il fattore 2-3 più lento di programmi equivalenti in C ++.

Basta dare un piccolo esempio: questo è un semplice codice di riferimento per unire le particelle nei cluster:

link

Vai a "scjet_cpp" e scrivi "make; make run". Questo esegue il codice C ++.  Quindi andare su "scjet_java" e digitare "make; make run". Questo funziona allo stesso modo  codice scritto in Java.

Il primo codice C ++ termina l'unione di particelle entro 50 msec (desktop i7). Esattamente lo stesso codice Java termina per 170 ms. Non c'è nulla di speciale nel codice, cioè un'implementazione identica in C ++ e Java.

Qualche commento?

    
posta IraS 10.02.2015 - 02:04
fonte

5 risposte

34

La tua misurazione è probabilmente difettosa.

  1. Se stai eseguendo entrambe le applicazioni e misura il tempo dall'inizio alla fine nel terminale, non stai misurando il tempo di esecuzione, ma anche il tempo di avvio. Per l'applicazione Java, ciò significa che quei 170 ms. contiene anche compilation JIT.

  2. Inoltre, 50 ms. contro 170 ms. non è rappresentativo Svolgere un'attività in un ciclo e ottenere 50 s. contro 170 s. sarà più pertinente.

  3. Non puoi semplicemente prendere un singolo progetto e dire: "Questo linguaggio è migliore, perché questo particolare progetto viene eseguito più velocemente con questo linguaggio." Un singolo progetto non è abbastanza rappresentativo. Forse gli autori conoscevano C ++ più di Java. Forse hanno perso opportunità per ottimizzare il codice.

  4. I compilatori che usi possono fare un'enorme differenza. Alcuni creeranno il codice con prestazioni terribili. Altri useranno tecniche intelligenti di ottimizzazione.

Inoltre, qual è il tuo obiettivo? Per essere in grado di dire: "Java fa schifo, tutti dovrebbero usare C ++?" Questo non è particolarmente costruttivo. Un approccio più costruttivo è costruire un'applicazione usando la tecnologia che il tuo team conosce meglio (ad esempio Java), quindi trova il collo di bottiglia e trova che può essere rimosso solo spostando questa parte del progetto in un linguaggio di basso livello (incluso Assembler).

    
risposta data 10.02.2015 - 02:22
fonte
16

Non c'è una risposta a questo, in gran parte perché non è sempre vero. In realtà, probabilmente non è mai vero. Non puoi misurare la velocità di un linguaggio, ma solo la velocità di qualche particolare implementazione (e raramente persino l'implementazione nel suo complesso, solo della sua velocità di esecuzione su qualche particolare pezzo di codice.

Il codice Java può essere più lento del codice C ++, a volte di un fattore molto più grande di 2 o 3.

Il codice Java può anche essere competitivo con il codice C ++, ma spesso richiede molta più memoria per farlo.

Per alcuni, molto specifici, Java può essere più veloce del C ++ che è scritto in modo simile.

Detto questo, sì, in media il codice scritto in Java verrà eseguito più lentamente di (approssimativamente) codice equivalente scritto in C ++. Se la tua preoccupazione principale è con la velocità di esecuzione, Java ha un numero di svantaggi.

Il principale è che dipende molto da un compilatore JIT per l'ottimizzazione - ma poiché l'utente è in attesa mentre è in esecuzione il compilatore JIT, la maggior parte dei compilatori JIT non include le ottimizzazioni più costose. Dal momento che un compilatore C ++ funziona generalmente solo con uno sviluppatore in attesa (o nessuno in attesa, nel caso di build basate su server con sistemi CI e simili) è molto più ragionevole che il compilatore includa ogni ottimizzazione nel libro.

Un secondo fattore è strettamente correlato: i venditori C ++ considerano i propri clienti estremamente interessati alla velocità. I venditori di Java vedono quasi certamente i propri clienti come più interessati a cose come le caratteristiche e lo sviluppo veloce rispetto a raggiungere la massima velocità di esecuzione possibile. Questo non vuol dire che possano o non debbano trascurare completamente la velocità di esecuzione, solo che non lo enfatizzano quasi allo stesso livello dei venditori C ++.

Anche la raccolta dei dati inutili può avere un effetto. Java (normalmente) utilizza un programma di raccolta simultaneo e di copia. Funziona abbastanza bene su una vasta gamma di carichi di lavoro, e la maggior parte delle JVM ha alcune "manopole di sintonia" per aiutare quando le impostazioni predefinite non sono ottimali.

Tuttavia, i test sembrano indicare che una JVM richiede sostanzialmente più memoria di un equivalente codice C ++ per sperare di funzionare alla stessa velocità (approssimativamente). Soprattutto quando si esegue una situazione limitata alla memoria, il sovraccarico della raccolta dei dati obsoleti può aumentare notevolmente. Questo in genere deriva meno dalla frequenza con cui il garbage collector viene eseguito (di per sé), piuttosto che dal fatto che quando viene eseguito, molti oggetti sono ancora "vivi". Un collezionista di copie lavora copiando oggetti "dal vivo" (raggiungibili) in una nuova posizione, quindi idealmente vuoi che quasi tutti gli oggetti siano "morti" (irraggiungibili) prima di essere eseguiti. Con meno memoria, non solo viene eseguito più spesso, ma ogni iterazione richiede più tempo perché meno oggetti hanno abbastanza età per "morire" e più oggetti rimangono raggiungibili (e quindi devono essere copiati).

C'è anche una differenza nella quantità di lavoro che viene eseguita in fase di esecuzione rispetto al tempo di compilazione. C ++ pone molta enfasi sul fare il più possibile in fase di compilazione. Ad esempio, tutte le "cose" interessanti del modello si verificano interamente in fase di compilazione (a scapito di tempi di compilazione orribilmente lunghi in alcuni casi). Nonostante la sintassi simile, gran parte (la maggior parte?) Dell'applicazione dei tipi di Java nel suo sistema generico avviene in fase di esecuzione. Java include anche (e molti programmi Java usano) cose come la reflection, che semplicemente non sono presenti in C ++.

Java aggiunge anche una serie di funzioni di comodità come il box automatico che rendono abbastanza facile aggiungere un po 'di overhead a operazioni apparentemente semplici in modi che non sono immediatamente evidenti. Anche il C ++ ne ha alcuni, ma (sembra a me) sono entrambi in numero minore, e i loro effetti dannosi sono in genere meno drastici (ma la mia opinione su questo potrebbe essere influenzata dal fatto che conosco abbastanza C ++ un po 'meglio, quindi per me, problemi simili possono sembrare molto più ovvi in C ++ che in Java).

In conclusione: è difficile indicare un singolo motivo specifico per cui i programmi Java sono più lenti dei programmi C ++. Sebbene sia vero il più delle volte, le ragioni del suo accadere variano molto, e in molti casi sembra essere più la "morte di mille tagli" che essere il risultato di un singolo fattore.

    
risposta data 10.02.2015 - 04:44
fonte
1

Sul perché Java può essere più lento: -

  • La JVM deve interpretare lo pseudo codice macchina, C ++ esegue ciecamente codice macchina nativo.
  • La JVM gestisce la memoria, C ++ è fino al programmatore per gestire la memoria che è più efficiente ma anche più incline agli errori.
  • La JVM deve essere avviata, quindi caricare e interpretare i vari classi; con un carico del programma C ++ sta fondamentalmente copiando il file istruzioni da disco a memoria, in molti casi (con DLL, .so, condivisa librerie ecc.) il programma sarà già stato caricato ed è seduto nella memoria pronta per andare. Questo è in realtà il più grande successo in termini di prestazioni con Java e rende la lingua totalmente inadatta alla breve esecuzione esecuzioni indipendenti.

Sebbene java possa essere fino a tre volte più lento è spesso quasi veloce come C ++. Devi chiederti se questo è davvero importante quando stai lavorando su un processore 3ghz che sta trascorrendo la maggior parte del tempo in attesa che venga caricata la memoria (o un IO disco peggiore).

L'altro lato dell'equazione (altamente soggettivo, ma supportato da anni di esperienza) è che i programmi Java possono essere sviluppati fino a tre volte più velocemente. I set di strumenti sono più funzionali, c'è una vasta scelta di librerie davvero buone tra cui scegliere e un numero limitato di trucchi da cercare (nessun if (a = b) , nessun puntatore di mixaggio con l'obiettivo).

Personalmente, dove sento il bisogno di velocità, codifico in chiaro C. È un linguaggio che conosco bene e le parti vincolate alle prestazioni di un sistema tendono ad essere piccole e ben definite.

Quindi la sua C per la velocità. Java per il codice di produzione. Groovy o Python per tutto il resto!

    
risposta data 10.02.2015 - 05:07
fonte
1

Per evitare di parlare di premesse false qui, mi piacerebbe vedere almeno parte dello smantellamento generato dagli hot spot (cicli stretti in cui la CPU passa la maggior parte del tempo) dai due programmi, prima di dire qualcosa di sostanziale qui.

Per Oracle JVM, è possibile utilizzare la seguente opzione per salvare una copia del codice macchina generato da JIT:

link

Per C ++ in esecuzione su x86, consiglio AMD CodeAnalyst su Windows e objdump su Linux.

Uno dovrebbe dare un'occhiata a:

  • La funzione chiama (quelle che non sono state sottolineate)
  • I loop (per vedere come sono nidificati e quante volte vengono eseguiti)
  • L'uso di istruzioni in virgola mobile
    • Indica se utilizza il vecchio stack x87 o la nuova istruzione SSE2 scalare / vettoriale in virgola mobile.
    • Uso di costose istruzioni in virgola mobile e prove di eventuali tecniche di ottimizzazione eseguite manualmente dall'autore originale di ciascun programma.
    • Uso delle chiamate alle librerie per la gestione delle funzioni matematiche

Se non conosci già i punti caldi del programma, usa un profilo di CPU Low Overhead per identificarli prima. Assicurati di non confondere con i profiler high-overhead. Sono pensati per scopi diversi.

    
risposta data 10.02.2015 - 05:22
fonte
1

Sono molto contento di vedere risposte professionali. Sei tutto intelligente!  Effettivamente, è possibile eseguire un piccolo controllo per verificare che questo lungo runtime in caso di Java sia dovuto alla compilazione JIT. Ecco come vedere questo:

Buste queste linee all'interno di scjet_java / KT.jet come

for (int i=0; i<1000; i++){
    KT KT = new KT(0.6, 1, -1, 5.0);
    KT.setDebug(true);
    KT.buildJets(list);
    KT.printJets();
}

Questo eseguirà il clustering di particelle 1000 volte. Vedrai che la prima esecuzione richiede circa 75 msec (i7,64 bit), mentre i successivi richiedono solo 15 ns per un passo di clustering. 15 ns è esattamente il tempo necessario per il codice C ++ all'interno di scjet_cpp / main.cpp per essere eseguito sul singolo evento!

Quindi, il runtime è simile in C ++ e Java. Non mi interessa molto della differenza del 10-20%, se ce ne sono a questo livello.

    
risposta data 10.02.2015 - 14:21
fonte

Leggi altre domande sui tag