it-swarm.com.de

scala vs Java, Leistung und Speicher?

Ich möchte gerne einen Blick auf Scala werfen und habe eine grundlegende Frage, auf die ich offenbar keine Antwort finden kann: Gibt es generell einen Unterschied in der Leistung und der Speichernutzung zwischen Scala und Java?

155
JohnSmith

Scala macht es sehr einfach, enorme Mengen an Speicher zu nutzen, ohne es zu merken. Dies ist normalerweise sehr leistungsfähig, kann aber gelegentlich ärgerlich sein. Angenommen, Sie haben ein Array von Zeichenfolgen (mit dem Namen array) und eine Zuordnung dieser Zeichenfolgen zu Dateien (mit dem Namen mapping). Angenommen, Sie möchten alle Dateien in der Map abrufen, die aus Zeichenfolgen mit einer Länge größer als zwei stammen. In Java könnten Sie

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);
}

Wütend! Harte Arbeit. In Scala ist die kompakteste Möglichkeit, dasselbe zu tun:

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

Einfach! Wenn Sie mit der Funktionsweise der Auflistungen nicht vertraut sind, werden Sie möglicherweise nicht feststellen, dass auf diese Weise ein zusätzliches Zwischenarray (mit filter) und ein zusätzliches Objekt für erstellt wurden. jedes Element des Arrays (mit mapping.get, das eine Option zurückgibt). Es werden auch zwei Funktionsobjekte erstellt (eines für den Filter und eines für die flatMap), obwohl dies selten ein großes Problem darstellt, da Funktionsobjekte klein sind.

Grundsätzlich ist die Speichernutzung auf einer primitiven Ebene dieselbe. Die Bibliotheken von Scala verfügen jedoch über viele leistungsstarke Methoden, mit denen Sie sehr einfach eine enorme Anzahl von (normalerweise kurzlebigen) Objekten erstellen können. Der Garbage Collector ist normalerweise ziemlich gut mit dieser Art von Müll umgehen, aber wenn Sie völlig vergessen, welcher Speicher verwendet wird, werden Sie wahrscheinlich eher in Scala als in Java auf Probleme stoßen.

Beachten Sie, dass der Code des Computersprachen-Benchmark-Spiels Scala in einem Java-ähnlichen Stil geschrieben ist, um eine Java-ähnliche Leistung zu erzielen, und daher eine Java-ähnliche Speichernutzung aufweist. Sie können dies in Scala tun: Wenn Sie Ihren Code so schreiben, dass er wie Hochleistungscode Java aussieht, ist er Hochleistungscode Scala. (Sie können in der Lage sein, es in einem idiomatischeren Scala Stil zu schreiben und trotzdem eine gute Leistung zu erzielen, aber es hängt von den Besonderheiten ab.)

Ich sollte hinzufügen, dass mein Scala Code pro Zeitaufwand für die Programmierung normalerweise schneller ist als mein Java Code, da ich in Scala die mühsame Leistungskritische Teile werden mit weniger Aufwand erstellt, und ich widme mehr meiner Aufmerksamkeit der Optimierung der Algorithmen und des Codes für die leistungskritischen Teile.

252
Rex Kerr

Ich bin ein neuer Benutzer, daher kann ich Rex Kerrs Antwort oben keinen Kommentar hinzufügen (es ist übrigens eine sehr seltsame Regel, neuen Benutzern zu erlauben, zu "antworten", aber nicht zu "kommentieren").

Ich habe mich einfach angemeldet, um auf die "Puh, Java ist so wortreich und so hart arbeitend" -Anspielung von Rex 'populärer Antwort oben zu antworten. Sie können natürlich auch prägnanter schreiben Scala Code, das angegebene Java Beispiel ist deutlich aufgebläht. Die meisten Java Entwickler würden so etwas codieren:

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

Und natürlich, wenn wir so tun, als ob Eclipse nicht den größten Teil der eigentlichen Eingabe für Sie erledigt und jedes gespeicherte Zeichen Sie wirklich zu einem besseren Programmierer macht, könnten Sie Folgendes codieren:

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

Jetzt habe ich mir nicht nur die Zeit gespart, die ich für die Eingabe vollständiger Variablennamen und geschweifter Klammern benötigte (wodurch ich 5 Sekunden mehr Zeit hatte, um tiefgründige algorithmische Überlegungen anzustellen), sondern ich kann meinen Code auch bei Verschleierungswettbewerben eingeben und möglicherweise zusätzliches Geld für verdienen die Feiertage.

100
Not Sleeping

Schreiben Sie Ihr Scala wie Java, und Sie können erwarten, dass nahezu identischer Bytecode ausgegeben wird - mit nahezu identischen Metriken.

Schreiben Sie es "idiomatischer", mit unveränderlichen Objekten und Funktionen höherer Ordnung, und es wird ein bisschen langsamer und ein bisschen größer. Die einzige Ausnahme von dieser Faustregel ist die Verwendung von generischen Objekten, bei denen die Typparameter das @specialised Anmerkung, dies erzeugt einen noch größeren Bytecode, der die Leistung von Java übertreffen kann, indem Boxen/Unboxen vermieden wird.

Erwähnenswert ist auch die Tatsache, dass mehr Speicher/weniger Geschwindigkeit ein unvermeidlicher Kompromiss ist, wenn Code geschrieben wird, der parallel ausgeführt werden kann. Idiomatischer Scala Code ist von Natur aus weitaus aussagekräftiger als typischer Java Code und besteht häufig nur aus 4 Zeichen (.par) nicht vollständig parallel sein.

Also wenn

  • Scala-Code dauert 1,25x länger als Java Code in einem einzelnen Thread
  • Es kann leicht auf 4 Kerne aufgeteilt werden (jetzt auch in Laptops üblich)
  • für eine parallele Laufzeit von (1.24/4 =) 0.3125x das ursprüngliche Java

Würden Sie dann sagen, dass der Scala Code jetzt vergleichsweise 25% langsamer oder 3x schneller ist?

Die richtige Antwort hängt genau davon ab, wie Sie "Leistung" definieren :)

64
Kevin Wright

Computersprachen-Benchmarks-Spiel:

Geschwindigkeitstest Java/Scala 1.71/2.25

Speichertest Java/Scala 66.55/80.81

Diese Benchmarks besagen also, dass Java ist 24% schneller und scala verbraucht 21% mehr Speicher.

Alles in allem ist es keine große Sache und sollte in Apps der realen Welt, in denen Datenbank und Netzwerk die meiste Zeit verbrauchen, keine Rolle spielen.

Fazit: Wenn Scala) Sie und Ihr Team (und die Leute, die das Projekt übernehmen, wenn Sie gehen) produktiver macht, dann solltest du es versuchen.

31
Peter Knego

Andere haben diese Frage in Bezug auf enge Schleifen beantwortet, obwohl es einen offensichtlichen Leistungsunterschied zwischen den Beispielen von Rex Kerr zu geben scheint, die ich kommentiert habe.

Diese Antwort richtet sich vor allem an Personen, die einen Optimierungsbedarf im engen Regelkreis als Konstruktionsfehler untersuchen.

Ich bin relativ neu in Scala (ungefähr ein Jahr oder so), aber das Gefühl bisher ist, dass es Ihnen erlaubt, aufzuschieben viele Aspekte des Designs, der Implementierung und Ausführung relativ einfach (mit genügend Hintergrundlesen und Experimentieren :)

Aufgeschobene Konstruktionsmerkmale:

Aufgeschobene Implementierungsfunktionen:

Aufgeschobene Ausführungsfunktionen: (Entschuldigung, keine Links)

  • Thread-sichere Lazy-Werte
  • Pass-by-Name
  • Monadisches Zeug

Für mich sind diese Funktionen diejenigen, die uns helfen, den Weg zu schnellen, engen Anwendungen zu beschreiten.


Die Beispiele von Rex Kerr unterscheiden sich darin, welche Aspekte der Ausführung aufgeschoben werden. Im Beispiel Java wird die Zuweisung des Speichers so lange verschoben, bis die Größe berechnet wird, wobei das Beispiel Scala die Mapping-Suche verzögert. Für mich scheinen sie völlig andere Algorithmen zu sein.

Hier ist, wie ich finde, eher eine Äpfel-zu-Äpfel-Entsprechung für sein Beispiel Java:

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

Keine zwischengeschalteten Sammlungen, keine Instanzen von Option usw. Dadurch wird auch der Sammlungstyp beibehalten, sodass der Typ von bigEnoughArray[File] - Array der Implementierung von collect entspricht Wahrscheinlich tun sie etwas in der Art, wie es der Java Code von Herrn Kerr tut.

Die oben aufgelisteten verzögerten Entwurfsfunktionen würden es Scalas Sammlungs-API-Entwicklern ermöglichen, diese schnelle Array-spezifische Sammlungsimplementierung in zukünftigen Releases zu implementieren, ohne die API zu beschädigen. Damit meine ich den Weg zur Geschwindigkeit.

Ebenfalls:

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

Die withFilter -Methode, die ich hier anstelle von filter verwendet habe, behebt das Problem mit der Zwischensammlung, aber es gibt immer noch das Problem mit der Optionsinstanz.


Ein Beispiel für die einfache Ausführungsgeschwindigkeit in Scala ist die Protokollierung.

In Java schreiben wir vielleicht etwas wie:

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

In Scala ist dies nur:

logger.debug("trace")

weil der in Scala zu debuggende Nachrichtenparameter den Typ "=> String" hat, den ich als parameterlose Funktion betrachte, die ausgeführt wird, wenn sie ausgewertet wird, die aber in der Dokumentation als Pass-by bezeichnet wird -Name.

EDIT {Funktionen in Scala sind Objekte, daher gibt es hier ein zusätzliches Objekt. Für meine Arbeit ist das Gewicht eines trivialen Objekts es wert, die Möglichkeit zu beseitigen, dass eine Protokollnachricht unnötig ausgewertet wird. }

Dies macht den Code nicht schneller, aber es ist wahrscheinlicher, dass er schneller ist, und es ist weniger wahrscheinlich, dass wir die Erfahrung haben, den Code anderer Leute massenhaft zu durchlaufen und zu bereinigen.

Für mich ist dies ein einheitliches Thema in Scala.


Harter Code kann nicht erfassen, warum Scala schneller ist, obwohl dies ein wenig andeutet.

Ich bin der Meinung, dass dies eine Kombination aus der Wiederverwendung von Code und der Obergrenze der Codequalität in Scala ist.

In Java wird großartiger Code oft zu einem unverständlichen Durcheinander und ist daher in APIs mit Produktionsqualität nicht wirklich realisierbar, da die meisten Programmierer ihn nicht verwenden könnten.

Ich habe große Hoffnungen, dass Scala die einsteins unter uns in die Lage versetzen könnte, weitaus kompetentere APIs zu implementieren, die möglicherweise über DSLs zum Ausdruck kommen. Die Kern-APIs in Scala befinden sich bereits weit auf diesem Weg.

20
Seth

@ higherkinded ´s Präsentation zum Thema - Scala Performance Considerations die einige Java/Scala-Vergleiche durchführt.

Werkzeuge:

Toller Blogpost:

11
oluies

Java und Scala kompilieren beide auf JVM-Bytecode, der Unterschied ist also nicht so groß. Der beste Vergleich, den Sie erhalten können, ist wahrscheinlich das Computersprachen-Benchmark-Spiel , was im Wesentlichen besagt, dass Java und Scala beide die gleiche Speicherbelegung haben. Scala ist nur etwas langsamer als Java auf einigen der aufgeführten Benchmarks, aber das könnte einfach daran liegen, dass die Implementierung der Programme unterschiedlich ist.

Wirklich, beide sind sich so nahe, dass es sich nicht lohnt, sich Sorgen zu machen. Die Produktivitätssteigerung, die Sie durch die Verwendung einer aussagekräftigeren Sprache wie Scala) erzielen, ist weitaus mehr wert als nur minimale (wenn überhaupt) Leistungseinbußen.

10
ryeguy

Das Beispiel Java ist wirklich keine Redewendung für typische Anwendungsprogramme. Solch ein optimierter Code kann in einer Systembibliotheksmethode gefunden werden. Dann würde es ein Array des richtigen Typs verwenden, d. H. File [], und keine IndexOutOfBoundsException auslösen. (Unterschiedliche Filterbedingungen zum Zählen und Hinzufügen). Meine Version wäre (immer (!) Mit geschweiften Klammern, weil ich nicht gerne eine Stunde damit verbringe, einen Fehler zu suchen, der durch Speichern der 2 Sekunden zum Drücken einer einzelnen Taste in Eclipse verursacht wurde.):

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);
    }
  }
}

Aber ich könnte Ihnen viele andere hässliche Java Codebeispiele aus meinem aktuellen Projekt bringen. Ich habe versucht, den üblichen Kopier- und Änderungsstil zu vermeiden, indem ich die üblichen Strukturen und Verhaltensweisen herausgerechnet habe.

In meiner abstrakten DAO-Basisklasse habe ich eine abstrakte innere Klasse für den allgemeinen Caching-Mechanismus. Für jeden konkreten Modellobjekttyp gibt es eine Unterklasse der abstrakten DAO-Basisklasse, in der die innere Klasse eine Implementierung für die Methode bereitstellt, die das Geschäftsobjekt erstellt, wenn es aus der Datenbank geladen wird. (Wir können kein ORM-Tool verwenden, da wir über eine proprietäre API auf ein anderes System zugreifen.)

Dieser Unterklassen- und Instanziierungscode ist in Java überhaupt nicht klar und in Scala sehr gut lesbar.

4
MickH