scala vs java, prestazioni e memoria? [chiuso]


160

Sono interessato a guardare Scala e ho una domanda di base a cui non riesco a trovare una risposta: in generale, c'è una differenza nelle prestazioni e nell'uso della memoria tra Scala e Java?


3
Ho sentito dire che le prestazioni possono essere molto vicine. Sospetto che dipenda fortemente da quello che stai facendo. (com'è per Java vs C)
Peter Lawrey

La risposta a questo tipo di domande è "dipende" - praticamente per qualsiasi confronto tra il sistema X e il sistema Y. Inoltre, questo è un duplicato di stackoverflow.com/questions/2479819/…
James Moore,

Risposte:


261

Scala rende molto facile usare enormi quantità di memoria senza accorgersene. Questo di solito è molto potente, ma a volte può essere fastidioso. Ad esempio, supponiamo di avere una matrice di stringhe (chiamata array) e una mappa da quelle stringhe ai file (chiamata mapping). Supponiamo di voler ottenere tutti i file che si trovano nella mappa e provengono da stringhe di lunghezza maggiore di due. In Java, potresti

int n = 0;
for (String s: array) {
  if (s.length > 2 && mapping.containsKey(s)) n++;
}
String[] bigEnough = new String[n];
n = 0;
for (String s: array) {
  if (s.length <= 2) continue;
  bigEnough[n++] = map.get(s);
}

Meno male! Lavoro duro. In Scala, il modo più compatto per fare la stessa cosa è:

val bigEnough = array.filter(_.length > 2).flatMap(mapping.get)

Facile! Ma, a meno che tu non abbia una certa familiarità con il funzionamento delle raccolte, ciò che potresti non capire è che questo modo di fare ciò ha creato un array intermedio extra (con filter) e un oggetto extra per ogni elemento dell'array (con mapping.get, che restituisce un opzione). Crea anche due oggetti funzione (uno per il filtro e uno per la mappa piatta), sebbene raramente ciò costituisca un grosso problema poiché gli oggetti funzione sono piccoli.

Quindi, sostanzialmente, l'utilizzo della memoria è, a livello primitivo, lo stesso. Ma le librerie di Scala hanno molti metodi potenti che ti consentono di creare un numero enorme di oggetti (di solito di breve durata) molto facilmente. Il garbage collector di solito è abbastanza bravo con quel tipo di immondizia, ma se si ignora completamente quale memoria viene utilizzata, probabilmente si verificheranno problemi a Scala prima di Java.

Si noti che il codice Scala del gioco Computer Language Benchmark è scritto in uno stile piuttosto Java per ottenere prestazioni simili a Java, e quindi ha un utilizzo della memoria simile a Java. Puoi farlo in Scala: se scrivi il tuo codice per sembrare un codice Java ad alte prestazioni, sarà un codice Scala ad alte prestazioni. (Si può essere in grado di scrivere in uno stile più idiomatica Scala e ancora ottenere buone prestazioni, ma dipende dai dettagli.)

Dovrei aggiungere che per la quantità di tempo impiegato nella programmazione, il mio codice Scala è generalmente più veloce del mio codice Java poiché in Scala posso ottenere le parti noiose non critiche per le prestazioni fatte con meno sforzo e dedicare più attenzione a ottimizzare gli algoritmi e codice per le parti critiche per le prestazioni.


172
+1 per l'ultimo paragrafo. È un punto vitale che viene trascurato troppo spesso.
Kevin Wright,

2
Ho pensato che le opinioni potessero essere di grande aiuto per le questioni che menzioni. O non è vero con gli array, in particolare?
Nicolas Payette,

1
@Kevin Wright - "È un punto vitale che viene trascurato troppo spesso" - È qualcosa che è facile da dire e difficile da dimostrare, e ci dice qualcosa sulle abilità di Rex Kerr e non su ciò che gli altri meno abili raggiungono.
igouy,

1
>> in stile idiomatico con prestazioni superlative << C'è spazio nel gioco di benchmark per programmi Scala idiomatici che non ottengono prestazioni "superlative".
igouy,

2
@RexKerr - il tuo esempio Java non cerca la chiave di mappatura due volte per ogni possibile stringa in cui l'esempio Scala lo fa solo una volta dopo aver selezionato le stringhe? Cioè sono ottimizzati in modi diversi per diversi set di dati?
Seth,

103

Sono un nuovo utente, quindi non sono in grado di aggiungere un commento alla risposta di Rex Kerr sopra (consentire ai nuovi utenti di "rispondere" ma non di "commentare" è una regola molto strana tra l'altro).

Mi sono iscritto semplicemente per rispondere all'insinuazione "accidenti, Java è così prolisso e così duro" della popolare risposta di Rex sopra. Mentre puoi ovviamente scrivere un codice Scala più conciso, l'esempio Java fornito è chiaramente gonfio. La maggior parte degli sviluppatori Java codifica qualcosa del genere:

List<String> bigEnough = new ArrayList<String>();
for(String s : array) {
  if(s.length() > 2 && mapping.get(s) != null) {
    bigEnough.add(mapping.get(s));
  }
}

E, naturalmente, se facciamo finta che Eclipse non esegua la maggior parte della digitazione effettiva per te e che ogni personaggio salvato ti rende davvero un programmatore migliore, allora potresti codificare questo:

List b=new ArrayList();
for(String s:array)
  if(s.length()>2 && mapping.get(s) != null) b.add(mapping.get(s));

Ora non solo ho risparmiato il tempo necessario a digitare nomi di variabili complete e parentesi graffe (liberandomi di spendere altri 5 secondi per pensare a pensieri algoritmici profondi), ma posso anche inserire il mio codice in concorsi di offuscamento e potenzialmente guadagnare denaro extra per le vacanze.


7
Come mai non sei un membro del club "hip language of the month"? Bei commenti. Mi è particolarmente piaciuto leggere l'ultimo paragrafo.
stepanian

21
Incredibilmente! Mi stanco degli esempi inventati in base ai quali il codice Java gonfiato è seguito da alcuni esempi concisi e concisi di Scala (o qualche altro linguaggio FP) e quindi una conclusione tratte affrettatamente che Scala deve essere migliore di Java a causa di esso. Chi ha mai scritto qualcosa di significativo in Scala comunque! ;-) E non dire Twitter ...
chrisjleu il

2
Bene, la soluzione di Rex prealloca la memoria per l'array, il che renderà più veloce l'esecuzione del codice compilato (perché con il tuo approccio, permetti alla JVM di riallocare periodicamente l'array man mano che cresce). Anche se è stato coinvolto un maggior numero di battiture, dal punto di vista delle prestazioni potrebbe essere un vincitore.
Ashalynd,

5
mentre ci siamo, in java8 sarà:Arrays.stream(array).map(mapping::get).filter(x->x!=null).toArray(File[]::new);
bennyl

2
Ciò che rende Scala "migliore" in qualche modo rispetto a Java sono le capacità espanse del sistema di tipi che semplificano l'espressione di schemi più generici come tipi (come Monadi, Functor, ecc.). Ciò consente di creare tipi che non si frappongono a causa di contratti troppo rigidi, come spesso accade in Java. Contratti rigorosi non basati su modelli reali nel codice sono la ragione per cui i modelli di inversione di responsabilità sono necessari solo per testare correttamente il codice (l'iniezione di dipendenza viene prima in mente e l'inferno XML che porta). L'addl. la concisione che la flessibilità porta è solo un vantaggio.
josiah,

67

Scrivi Scala come Java e puoi aspettarti che venga emesso un bytecode quasi identico, con metriche quasi identiche.

Scrivilo più "idiomaticamente", con oggetti immutabili e funzioni di ordine superiore, e sarà un po 'più lento e un po' più grande. L'unica eccezione a questa regola empirica è quando si usano oggetti generici in cui i parametri di tipo usano l' @specialisedannotazione, questo creerà un bytecode ancora più grande che può superare le prestazioni di Java evitando boxe / unboxing.

Vale anche la pena ricordare che una maggiore quantità di memoria / minore velocità è un inevitabile compromesso durante la scrittura di codice che può essere eseguito in parallelo. Il codice Scala idiomatico ha una natura molto più dichiarativa rispetto al tipico codice Java ed è spesso a soli 4 caratteri ( .par) dall'essere completamente parallelo.

Quindi se

  • Il codice Scala impiega 1,25 volte più del codice Java in un singolo thread
  • Può essere facilmente suddiviso su 4 core (ora comune anche nei laptop)
  • per un tempo di esecuzione parallelo di (1,24 / 4 =) 0,3125x Java originale

Diresti quindi che il codice Scala è ora relativamente più lento del 25% o 3 volte più veloce?

La risposta corretta dipende esattamente da come si definisce "performance" :)


4
Per inciso, potresti voler menzionare che .parè in 2.9.
Rex Kerr,

26
>> Diresti allora che il codice Scala è ora relativamente più lento del 25% o 3 volte più veloce? << Direi perché il tuo ipotetico confronto con il codice Java multi-thread?
igouy,

17
@igouy - Il punto è che detto codice ipotetico non esiste, la natura imperativa del codice Java "più veloce" rende molto più difficile il parallelismo, in modo che il rapporto costi / benefici significhi che è improbabile che accada affatto. La Scala idiomatica, d'altra parte, essendo molto più dichiarativa in natura può essere spesso fatta in concomitanza con un semplice cambiamento.
Kevin Wright,

7
L'esistenza di programmi Java simultanei non implica che un tipico programma Java possa essere facilmente adattato alla concorrenza. Semmai, direi che un particolare stile fork-join è particolarmente raro in Java e deve essere esplicitamente codificato, mentre operazioni semplici come la ricerca del valore minimo contenuto o la somma dei valori in una raccolta possono essere banalmente eseguite in parallelo in Scala semplicemente usando .par.
Kevin Wright,

5
No, forse no. Questo genere di cose è un elemento fondamentale per molti algoritmi e vederlo presente a un livello così basso nel linguaggio e nelle librerie standard (le stesse librerie standard che verranno utilizzate da tutti i programmi, non solo quelle tipiche) è la prova che " sei già più vicino ad essere simultaneo semplicemente scegliendo la lingua. Ad esempio, la mappatura su una raccolta è intrinsecamente adatta alla parallelizzazione e il numero di programmi Scala che non utilizzano il mapmetodo sarà vanificante.
Kevin Wright,

31

Gioco di benchmark dei linguaggi informatici:

Test di velocità java / scala 1.71 / 2.25

Test di memoria java / scala 66.55 / 80.81

Quindi, questo benchmark afferma che java è più veloce del 24% e che scala utilizza il 21% di memoria in più.

Tutto sommato non è un grosso problema e non dovrebbe importare nelle app del mondo reale, dove la maggior parte del tempo viene consumata dal database e dalla rete.

In conclusione: se Scala rende te e il tuo team (e le persone che prendono il progetto quando parti) più produttivi, allora dovresti provarci.


34
Dimensione codice java / scala 3.39 / 2.21
hammar

22
Fai attenzione con numeri come questi, sembrano terribilmente precisi mentre in realtà non significano quasi nulla. Non è come se Scala sia sempre il 24% più veloce di Java in media, ecc.
Jesper,

3
I numeri citati in Afaik indicano il contrario: Java è il 24% più veloce dello scala. Ma come dici tu, sono microbenchmark, che non devono corrispondere a ciò che sta accadendo nelle app reali. E il diverso modo o soluzione dei problemi in lingue diverse potrebbe portare a programmi meno comparabili alla fine.
utente sconosciuto

9
"Se Scala fa te e la tua squadra ..." In conclusione: lo saprai dopo non prima :-)
igouy

La pagina di aiuto del gioco di benchmark fornisce un esempio di come "confrontare velocità e dimensioni del programma per implementazioni in 2 lingue". Per Scala e Java la pagina web di confronto appropriata è - shootout.alioth.debian.org/u64q/scala.php
igouy,

20

Altri hanno risposto a questa domanda per quanto riguarda i loop stretti, anche se sembra esserci un'ovvia differenza di prestazioni tra gli esempi di Rex Kerr che ho commentato.

Questa risposta è davvero indirizzata a persone che potrebbero indagare sulla necessità di ottimizzazione a circuito chiuso come difetto di progettazione.

Sono relativamente nuovo in Scala (circa un anno circa) ma la sensazione, finora, è che ti consente di differire molti aspetti del design, dell'implementazione e dell'esecuzione in modo relativamente semplice (con una lettura e sperimentazione di sfondo sufficienti :)

Caratteristiche di progettazione differite:

Caratteristiche di implementazione differite:

Funzionalità di esecuzione differite: (scusa, nessun link)

  • Valori pigri thread-safe
  • Pass-by-name
  • Roba monadica

Queste caratteristiche, per me, sono quelle che ci aiutano a percorrere il percorso verso applicazioni veloci e strette.


Gli esempi di Rex Kerr differiscono in quali aspetti dell'esecuzione sono rinviati. Nell'esempio Java, l'allocazione della memoria viene rinviata fino a quando non viene calcolata la sua dimensione laddove l'esempio Scala difende la ricerca della mappatura. A me sembrano algoritmi completamente diversi.

Ecco cosa penso sia più di una mela alle mele equivalente per il suo esempio Java:

val bigEnough = array.collect({
    case k: String if k.length > 2 && mapping.contains(k) => mapping(k)
})

Nessuna raccolta intermedia, nessuna Optionistanza ecc. Ciò preserva anche il tipo di raccolta, quindi bigEnoughil tipo è Array[File]: Arrayl' collectimplementazione probabilmente farà qualcosa in linea con ciò che fa il codice Java di Mr Kerr.

Le funzionalità di progettazione differite che ho elencato sopra consentirebbero anche agli sviluppatori dell'API di raccolta di Scala di implementare quella rapida implementazione di raccolta specifica dell'array nelle versioni future senza interrompere l'API. Questo è ciò a cui mi riferisco mentre percorro il cammino verso la velocità.

Anche:

val bigEnough = array.withFilter(_.length > 2).flatMap(mapping.get)

Il withFiltermetodo che ho usato qui invece di filterrisolvere il problema della raccolta intermedia ma c'è ancora il problema dell'istanza Option.


Un esempio di semplice velocità di esecuzione in Scala è con la registrazione.

In Java potremmo scrivere qualcosa del tipo:

if (logger.isDebugEnabled())
    logger.debug("trace");

Alla Scala, questo è solo:

logger.debug("trace")

perché il parametro del messaggio da eseguire il debug in Scala ha il tipo " => String" che considero una funzione senza parametri che viene eseguita quando viene valutata, ma che la documentazione chiama pass-by-name.

EDIT {Le funzioni in Scala sono oggetti, quindi qui c'è un oggetto in più. Per il mio lavoro, il peso di un oggetto banale vale la pena rimuovere la possibilità che un messaggio di registro venga valutato inutilmente. }

Questo non rende il codice più veloce, ma rende più probabile che sia più veloce e abbiamo meno probabilità di avere l'esperienza di esaminare e ripulire il codice di altre persone in massa.

Per me, questo è un tema coerente all'interno di Scala.


Il codice hard non riesce a capire perché Scala è più veloce, anche se suggerisce un po '.

Sento che è una combinazione di riutilizzo del codice e il massimo della qualità del codice in Scala.

In Java, il fantastico codice è spesso costretto a diventare un pasticcio incomprensibile e quindi non è realmente praticabile all'interno delle API di qualità di produzione poiché la maggior parte dei programmatori non sarebbe in grado di usarlo.

Spero fortemente che Scala possa consentire a tutti gli utenti di implementare API molto più competenti, potenzialmente espresse tramite DSL. Le API principali di Scala sono già molto lontane su questo percorso.


Il materiale di registrazione è un buon esempio delle insidie ​​prestazionali di Scala: logger.debug ("trace") crea un nuovo oggetto per la funzione senza parametri.
jcsahnwaldt Reinstate Monica,

In effetti, in che modo influisce sul mio punto associato?
Seth,

I suddetti oggetti possono anche essere usati per creare strutture di controllo IoC trasparenti per motivi di efficienza. Sì, lo stesso risultato è teoricamente possibile in Java, ma sarebbe qualcosa che avrebbe influenzato / offuscato in modo drammatico il modo in cui il codice è stato scritto - quindi la mia argomentazione secondo cui il talento di Scala nel rinviare molti elementi dello sviluppo del software ci aiuta a spostarci verso un codice più veloce - è più probabile che sia più veloce nella pratica rispetto ad avere prestazioni dell'unità leggermente più veloci.
Seth,

Ok, ho riletto questo e ho scritto "velocità di esecuzione semplice" - aggiungerò una nota. Buon punto :)
Seth,

3
Istruzione if prevedibile (sostanzialmente gratuita su un processore superscalare) vs allocazione oggetto + immondizia. Il codice Java è ovviamente più veloce (nota che valuta solo la condizione, l'esecuzione non raggiungerà l'istruzione di registro.) In risposta a "Per il mio lavoro, vale la pena il peso di un oggetto banale rimuovere la possibilità che un messaggio di registro venga valutato inutilmente ".
Eloff,


10

Java e Scala si compilano entrambi in bytecode JVM, quindi la differenza non è così grande. Il miglior confronto che puoi ottenere è probabilmente sul gioco dei benchmark dei linguaggi per computer , che sostanzialmente dice che Java e Scala hanno entrambi lo stesso utilizzo di memoria. Scala è solo leggermente più lento di Java su alcuni dei benchmark elencati, ma ciò potrebbe essere semplicemente dovuto al fatto che l'implementazione dei programmi è diversa.

Davvero, sono entrambi così vicini che non vale la pena preoccuparsi. L'aumento della produttività che si ottiene utilizzando un linguaggio più espressivo come Scala vale molto di più del minimo (se presente) hit delle prestazioni.


7
Vedo un errore logico qui: entrambe le lingue si compilano in bytecode, ma un programmatore esperto e un novizio - il loro codice si compila anche in bytecode - ma non nello stesso bytecode, quindi la conclusione, che la differenza non può essere così grande , può essere sbagliato. E in effetti, in passato, un ciclo while potrebbe essere molto, molto più veloce in scala di un ciclo for equivalente semanticamente equivalente (se ricordo bene, oggi è molto meglio). Ed entrambi sono stati compilati in bytecode, ovviamente.
utente sconosciuto

@utente sconosciuto - "un ciclo while potrebbe essere molto, molto più veloce in scala rispetto a un ciclo semanticamente equivalente" - nota che quei programmi di gioco di benchmark Scala sono scritti con cicli while.
igouy,

@igouy: non ho parlato dei risultati di questo microbenchmark, ma dell'argomentazione. Una vera affermazione Java and Scala both compile down to JVM bytecode, che è stata combinata con una soalla dichiarazione in questione diffence isn't that big.che volevo dimostrare, che soè solo un trucco retorico e non una conclusione argomentativa.
utente sconosciuto

3
risposta sorprendentemente errata con voti sorprendentemente alti.
Shabunc,

4

L'esempio Java non è in realtà un linguaggio per i tipici programmi applicativi. Tale codice ottimizzato potrebbe essere trovato in un metodo di libreria di sistema. Ma poi userebbe un array del tipo giusto, cioè File [] e non genererebbe IndexOutOfBoundsException. (Diverse condizioni di filtro per il conteggio e l'aggiunta). La mia versione sarebbe (sempre (!) Con parentesi graffe perché non mi piace passare un'ora a cercare un bug che è stato introdotto salvando i 2 secondi per premere un singolo tasto in Eclipse):

List<File> bigEnough = new ArrayList<File>();
for(String s : array) {
  if(s.length() > 2) {
    File file = mapping.get(s);
    if (file != null) {
      bigEnough.add(file);
    }
  }
}

Ma potrei offrirti molti altri brutti esempi di codice Java dal mio progetto attuale. Ho cercato di evitare lo stile comune di copia e modifica della codifica prendendo in considerazione strutture e comportamenti comuni.

Nella mia classe base DAO astratta ho una classe interna astratta per il meccanismo di memorizzazione nella cache comune. Per ogni tipo di oggetto modello concreto esiste una sottoclasse della classe base DAO astratta, in cui la classe interna viene sottoclassata per fornire un'implementazione per il metodo che crea l'oggetto business quando viene caricato dal database. (Non possiamo utilizzare uno strumento ORM perché accediamo a un altro sistema tramite un'API proprietaria.)

Questo codice di sottoclasse e di istanza non è affatto chiaro in Java e sarebbe molto leggibile in Scala.

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.