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?
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?
Risposte:
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.
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.
Arrays.stream(array).map(mapping::get).filter(x->x!=null).toArray(File[]::new);
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' @specialised
annotazione, 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
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" :)
.par
è in 2.9.
.par
.
map
metodo sarà vanificante.
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.
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)
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 Option
istanza ecc. Ciò preserva anche il tipo di raccolta, quindi bigEnough
il tipo è Array[File]
: Array
l' collect
implementazione 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 withFilter
metodo che ho usato qui invece di filter
risolvere 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.
La presentazione di @higherkinded sull'argomento - Considerazioni sulle prestazioni della Scala che fa alcuni confronti Java / Scala.
Utensili:
Ottimo post sul blog:
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.
Java and Scala both compile down to JVM bytecode,
che è stata combinata con una so
alla dichiarazione in questione diffence isn't that big.
che volevo dimostrare, che so
è solo un trucco retorico e non una conclusione argomentativa.
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.