2016-04-02 9 views
5

MappedByteBuffer (Memory-Mapped-Datei in Java) Um zu versuchen, schrieb ich eine einfache wc -l (Textdatei Zeilenzahl) Demo:Warum ist dieses Programm "Zeilenanzahl" in Java langsam? Mit MappedByteBuffer

int wordCount(String fileName) throws IOException { 
    FileChannel fc = new RandomAccessFile(new File(fileName), "r").getChannel(); 
    MappedByteBuffer mem = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); 

    int nlines = 0; 
    byte newline = '\n'; 

    for(long i = 0; i < fc.size(); i++) { 
     if(mem.get() == newline) 
      nlines += 1; 
    } 

    return nlines; 
} 

Ich habe versucht, dies auf eine Datei von etwa 15 MB (15.008.641 Byte), und 100k Linien. Auf meinem Laptop dauert es etwa 13.8 sec. Warum ist es so langsam?

kompletter Klassencode ist hier: http://pastebin.com/t8PLRGMa

für den Hinweis, ich schrieb die gleiche Idee in C: http://pastebin.com/hXnDvZm6

Es läuft in etwa 28 ms oder 490 times faster.

Aus Neugier, schrieb ich auch eine Scala-Version mit im Wesentlichen den gleichen Algorithmus und APIs wie in Java. Es läuft 10 times faster, was darauf hindeutet, es ist definitiv etwas seltsam los.

Aktualisierung: Die Datei wird vom Betriebssystem zwischengespeichert, daher ist keine Ladezeit für die Datenträger erforderlich.

Ich wollte Speicherzuordnung für den wahlfreien Zugriff auf größere Dateien verwenden, die nicht in RAM passen. Deshalb benutze ich nicht nur einen BufferedReader.

+0

Java-Version: OpenJDK 1.8.0 Plattform: Linux 4.1.16 – cidermole

+0

'MappedByteBuffer' ist die falsche Sache zu verwenden, Ihr Programm braucht nichts anderes als eine Ebene' BufferedReader '. Sie verwenden keine der erweiterten Funktionen des 'MappedByteBuffer', also warum? –

+1

Ich tippte eine Antwort, aber die Frage war geschlossen. Ihr Code ist langsam, weil Byte für Byte gelesen wird, und das ist sehr langsam. Lesen Sie Puffer nach Puffer, und die Leistung wird drastisch erhöht. Wenn Sie beispielsweise https://gist.github.com/jnizet/21341d48f631b7f10bc657e560c0f2de verwenden, beträgt die aufgewendete Zeit 50493us. vs 8646279us. für deine Originalversion. Aber ich stimme zu, dass ein BufferedInputStream sowieso einfacher wäre. –

Antwort

10

Der Code ist sehr langsam, weil fc.size() in der Schleife aufgerufen wird.

JVM kann fc.size() offensichtlich nicht beseitigen, da Dateigröße in Laufzeit geändert werden kann. Das Abfragen der Dateigröße ist relativ langsam, da ein Systemaufruf für das zugrunde liegende Dateisystem erforderlich ist.

Ändern Sie diese zu

long size = fc.size(); 
    for (long i = 0; i < size; i++) { 
     ... 
    } 
+1

Haha, ich lag völlig falsch. Dies ist in der Tat der Grund. –

+1

AUTSCH! Es musste etwas wirklich Dummes sein. Vielen Dank! Jetzt läuft in 73 ms, oder 2,6-mal unter der C-Leistung. – cidermole

+0

@cidermole einschließlich JVM Startup? –

Verwandte Themen