2016-08-20 5 views
4

Ich habe ein Stück Code, wenn ich eine Karte erstellen wie:Scala unveränderlich Karte langsam

val map = gtfLineArr(8).split(";").map(_ split "\"").collect { case Array(k, v) => (k, v) }.toMap 

Dann habe ich diese Karte verwenden, um meine Aufgabe zu erstellen:

case class MyObject(val attribute1: String, val attribute2: Map[String:String]) 

Ich lese Millionen Linien und Konvertierung in MyObjects mit einem Iterator. Wie

MyObject("1", map) 

Wenn ich es wirklich langsam ist, mehr als 1 Stunde für 2'000'000 Einträge.

Ich entferne die Karte von der Objekterstellung, aber noch kann ich den Split-Prozess (Teil 1):

val map = gtfLineArr(8).split(";").map(_ split "\"").collect { case Array(k, v) => (k, v) }.toMap 
MyObject("1", null) 

und den Prozess das Skript in weniger als 1 min laufen. für die 2'000'000 Millionen Einträge.

Ich habe etwas Profiling und sieht so aus ist, wenn das Objekt erstellt wird die Zuordnung zwischen val map auf die Objektkarte macht den Prozess langsam. Was ich vermisse?

-Update das Problem besser erklären:

Wenn Sie meinen Code sehen das mein Selbst Iterierte über 2.000.000 Zeilen an eine interne objet Umwandlung jeder Zeile zu erklären, ich iterieren:

it.map(cretateNewObject).toList 

Dieser Iterator durchläuft alle Zeilen und konvertiert sie mit der Funktion createNewObject in meine Objekte.

Das ist wirklich sehr schnell, speziell mit großen Speicher wie dk14 sagte. Das Performance-Problem ist in meiner

`crateNewObject(val line:String)` 

dieser Funktion ein

Objekt erstellen
`class MyObject(val attribute1:String, val attribute2:Map[String, String])` 

die meine Funktion die Leitung übernehmen und tun erste

`val attributeArr = line.split("\t")` 

der erste Attributsatz des Arrays das ist Attribut1 meines Objekts und das zweite Attribut ist

`val map = attributeArr(8).split(";").map(_ split "\"").collect { case Array(k, v) => (k, v) }.toMap` 

Wenn ich nur die Anzahl der Elemente in Karte drucken, enden die Programme in 2 min, wenn ich Karte zu meiner neuen Objektlinie MyObject(attribute1, map) übergebe, ist das Programm wirklich langsam.

+0

Im zweiten Fall verwenden Sie wahrscheinlich 'val map' nicht irgendwo, so dass der Compiler nur den Code in dieser Zeile wegwirft und nichts teilt. – Kolmar

+0

Hi @kolman Das ist nicht der Fall, weil 'gtfLineArr (8) .split ("; "). Map (_ split" \ ""). Collect {case Array (k, v) => (k, v)} .toMap "berechnet den Split und konvertiert das Ergebnis in eine Map. Tatsächlich habe ich' gtfLineArr (8) .split (";"). map (_split "\" "). collect {case Array (k, v) => (k, v)} .toMap.size' und drucke die Ergebnisse und ist eine Minute, die bedeutet, dass die Map erstellt wurde. Meine verdächtige ist, dass, wenn die Karte an das neue Objekt übergeben wird, eine "gewisse" Transformation stattfindet, die das Problem verursacht. – ypriverol

+0

Was macht Ihr Code sonst noch? Die Zeit wird wahrscheinlich nicht in diesen Zeilen verbracht. – Dima

Antwort

3

(0 to 2000000).toList und (0 to 2000000).map(x => x -> x).toMap haben ähnliche Leistung, wenn Sie ihnen genug Speicher geben (Ich versuchte -Xmx4G - 4 Gigabyte). toMap Implementation ist eine Menge über das Klonen, so dass viel Speicher "zugewiesen"/"freigegeben" wird. Also, im Falle von Speichermangel wird GC überaktiv.

Als ich versuchte, mit 128 MB zu laufen - es dauerte einige Sekunden, aber (0 to 2000000).map(x => x -> x).toMap dauerte mindestens 2 Minuten mit 10% GC-Aktivität (VisualVM), und starb mit nicht genügend Arbeitsspeicher.

Allerdings, als ich versuchte -Xmx4G waren beide ziemlich schnell.


P.S. Was toMap tut, fügt wiederholt ein Element zu einer Präfix-Struktur hinzu, so dass es (Array.copy) viel pro Element klonen muss: https://github.com/scala/scala/blob/99a82be91cbb85239f70508f6695c6b21fd3558c/src/library/scala/collection/immutable/HashMap.scala#L321.

So toMap ist wiederholt (2.000.000 mal) tun updated0, der seinerseits ziemlich oft ein Array.copy tun, die viele Speicherzuordnungen erfordern, die (in Low-Memory-Fall) verursacht GC MarkAndSweep (langsame Garbage Collection gehen) die meiste Zeit (soweit ich von jconsole sehen kann).


Lösung: Ob der Speicher erhöhen (-Xmx/-Xms JVM-Parameter) oder wenn Sie komplexere Operationen auf Daten-Set Verwendung so etwas wie Apache Spark (oder jede chargenorientierte Karten reduzieren Rahmen) müssen verarbeiten Ihre Daten auf verteilte Weise.

+0

Ich weiß, dass die '.toMap' ist zu verbrauchen. Aber, wenn Sie das Beispiel sehen, wird die Operation nur für jeden Eintrag durchgeführt, nicht die Liste. – ypriverol

+0

Ich habe einige Profiling gemacht und sieht aus wie jedes Mal als 'Map [String, String]' gesammelt und die Karte an das Objekt (für jeden der 2 Millionen Einträge) eine Transformation von char [] nach String durchgeführt wird durchgeführt . – ypriverol

+0

der eigentliche Code, den ich versuche zu laufen, wie ich in meinem Beispiel gesagt habe, ist das folgende: – ypriverol