Thomas Kramer

IT-COW | All posts tagged 'StringBuilder'

Eclipse/Java: Vergleich von vier Varianten zum Datei-Laden

By Administrator at Dezember 12, 2011 13:42
Filed Under: Java, Programmierung allgemein

Da Processing wahrscheinlich eine angepasste Version von Java benutzt (da dort z. B. auch Code außerhalb von Klassen erlaubt ist) habe ich den StringBuilder-Vergleich nun auch für Eclipse-Java angepasst – man vergleiche mit dem vorherigen Beitrag für Processing/Java.

 

Voraussetzung für das erfolgreiche Durchführen war den Java-Heap Speicher in Run Configurations unter VM arguments anzupassen - aber nur die maximale Größe mit dem –Xmx-Parameter, mit dem –Xms-Parameter für die initiale/anfängliche Größe wäre der RAM-Vergleich nicht mehr korrekt.

 

Die Methode loadStrings() scheint Processing- und nicht Java-spezifisch zu sein, ich habe daher die letzte Lade-Variante durch die FileUtils.readFileToString-Methode der frei verfügbaren Commons IO-Bibliothek von Apache ersetzt. Gemäß diesem Link gibt es in den Standard-Java-Bibliotheken keine Möglichkeit um direkt in einen einzelnen String zu laden.

 

Insgesamt sind die Ergebnisse aber vergleichbar mit Processing/Java, ich wollte nur den Vergleich korrekt ausführen. Der StringBuilder in Java benötigt definitiv zusätzlichen RAM bei der .toString()-Methode, das Äquivalent im .NET-Framework dagegen nicht.

 

Damit der Java-Prozess nicht sofort wieder geschlossen wird lasse ich das Programm abschließend auf einen Tastendruck warten.

 

Dateigröße auf Festplatte: 65784159

 

1.1) zeilenweises Einlesen mit nötigem Overhead:

Dateigröße im RAM: 65784171
Overhead: 12 Zeichen
1390 ms Zeit benötigt zum Datei-Laden.

RAM-Verbrauch: 175 MB.

 

1.2) zeilenweises Einlesen ohne die nötigen 12 Zeichen Overhead: 

Dateigröße im RAM: 65784171
Overhead: 12 Zeichen
2021 ms Zeit benötigt zum Datei-Laden.

RAM-Verbrauch: 425 MB.

 

2) zeichenweises Einlesen:
Dateigröße im RAM: 65784159
5313 ms Zeit benötigt zum Datei-Laden.

RAM-Verbrauch: 125 MB.

 

3) in 1000-Zeichen-Blöcken einlesen:
Dateigröße im RAM: 65784159
964 ms Zeit benötigt zum Datei-Laden.

RAM-Verbrauch: 160 MB.

 

4) Laden mittels FileUtils der Apache-Bibliothek org.apache.commons.io:
Dateigröße im RAM: 65784159
Overhead: 0 Zeichen
1946 ms Zeit benötigt zum Datei-Laden.

RAM-Verbrauch: 260 MB.

 

Update 16.12.2011: Im Internet hat man mir die JConsole für das Monitoring von Java-Anwendungen empfohlen, sowie die Seite Java Performance Tuning. Die bisher erzielten Rückschlüsse sind für mich aber bereits ausreichend.

 

Update 17.01.2012: Den append-Befehl vom Stringbuilder gibt es überladen auch in dieser Variante: append(char[] str, int offset, int len). Dadurch kann man sich beim blockweisen Lesen den Rest nach der Schleife einsparen:

 

while ((buffer = br.read(ioBuf,0,1000)) != -1)
{
  /* eingelesene Zeile anhängen */
  result.append(ioBuf,0,buffer);
}

 

Eine Amerkung noch zum Quellcode - leider markiert BlogEngine.NET nach // häufig auch weitere nachfolgende Zeilen als Kommentarzeilen.

 

import java.lang.Math;
import java.util.concurrent.TimeUnit;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import org.apache.commons.io.FileUtils;

public class Program
{
  public String test = "";
  public int stringBuilderOverhead = 12;
  public int fileLength = 0;

  // systemeigenes Zeilenumbruchszeichen ermitteln
  String sep = System.getProperty("line.separator");       
  public String loadPath="C:/Users/thomas/Downloads/pg2600_2.txt";
  public StringBuilder completeString=new StringBuilder(0);
   
  /*-----------------------------------------------------------------------------
   *  Variante 1: zeilenweises Einlesen einer Textdatei mit StringBuilder-Overhead
   *-----------------------------------------------------------------------------*/
  void loadFile1()
  {       
    int overhead = 0;
    /* nimm die Vorher-Zeit für Gesamtdurchlauf */   
    long completeTimeBefore = System.currentTimeMillis();       
   
    completeString = new StringBuilder(fileLength + stringBuilderOverhead);              
    try
    {
      String thisLine=null;
      BufferedReader br = new BufferedReader(new FileReader(loadPath));     
      while ((thisLine = br.readLine()) != null)
      {
        /* eingelesene Zeile anhängen */
        completeString.append(thisLine);
        /* Zeilenumbruch hinzufügen */
        completeString.append(sep);
      }
      br.close();
    } catch (IOException e) {
      System.out.println("Datei konnte nicht eingelesen werden!");
      return;
    }
   
    System.out.println("Dateigröße im RAM: " + (overhead=completeString.length()));   
    System.out.println("Overhead: " + (overhead-fileLength) + " Zeichen");
 
    /* nimm die Danach-Zeit für Gesamtdurchlauf und bestimme Differenz */   
    long completeTimeAfter = System.currentTimeMillis();     
    long completeTimeDiff   = completeTimeAfter - completeTimeBefore;       
    System.out.println(completeTimeDiff + " ms Zeit benötigt zum Datei-Laden.\n"); 
  }

  /*-----------------------------------------------------------------------------
   *  Variante 2: zeichenweises Einlesen und zeichenweises Hinzufügen
   *-----------------------------------------------------------------------------*/

  void loadFile2()
  {       
    /* nimm die Vorher-Zeit für Gesamtdurchlauf */   
    long completeTimeBefore = System.currentTimeMillis();       
 
    completeString = new StringBuilder(fileLength);            
    try
    {
      int thisChar;
      BufferedReader br = new BufferedReader(new FileReader(loadPath));        
      while ((thisChar = br.read()) != -1)
      {
        /* eingelesene Zeile anhängen */
        completeString.append((char) thisChar);
      }
      br.close();
    } catch (IOException e)
    {
      System.out.println("Datei konnte nicht eingelesen werden!");
      return;
    }            
   
    System.out.println("Dateigröße im RAM: " + completeString.length());   
 
    /* nimm die Danach-Zeit für Gesamtdurchlauf und bestimme Differenz */   
    long completeTimeAfter = System.currentTimeMillis();     
    long completeTimeDiff   = completeTimeAfter - completeTimeBefore;       
    System.out.println(completeTimeDiff + " ms Zeit benötigt zum Datei-Laden.\n"); 
  }

  /*-----------------------------------------------------------------------------
   *  Variante 3: in 1000-Zeichen-Blöcken einlesen und hinzufügen
   *-----------------------------------------------------------------------------*/

  void loadFile3()
  {       
    /* nimm die Vorher-Zeit für Gesamtdurchlauf */   
    long completeTimeBefore = System.currentTimeMillis();       
     
    completeString = new StringBuilder(fileLength);            
    try
    {
      char[] ioBuf = new char[1000];     
      /* buffer liefer Anzahl gelesener Zeichen zurück, siehe auch
         http://www.dpunkt.de/java/Referenz/Das_Paket_java.io/4.html#read%28%29 */
 
      int buffer = 0;   
     
      BufferedReader br = new BufferedReader(new FileReader(loadPath));     
      while ((buffer = br.read(ioBuf,0,1000)) == 1000)      
      {
        /* eingelesene Zeile anhängen */
        completeString.append(ioBuf);
        /* Array wieder leeren falls nur weniger Zeichen eingelesen werden können */
        ioBuf = new char[1000];             
      }
      /* die letzten gelesenen Zeichen müssen auch hinzugefügt werden */
      /* -1 wäre wenn von vornherein gar kein Zeichen eingelesen werden konnte */
      if (buffer != -1)
      {
        for (int i=0;i<buffer;i++)
          completeString.append(ioBuf[i]);
      }
      br.close();
    } catch (IOException e)
    {
      System.out.println("Datei konnte nicht eingelesen werden!");
      return;
    }            
   
    System.out.println("Dateigröße im RAM: " + completeString.length());   
 
    /* nimm die Danach-Zeit für Gesamtdurchlauf und bestimme Differenz */   
    long completeTimeAfter = System.currentTimeMillis();     
    long completeTimeDiff   = completeTimeAfter - completeTimeBefore;       
    System.out.println(completeTimeDiff + " ms Zeit benötigt zum Datei-Laden.\n"); 
  }

  /*-----------------------------------------------------------------------------
   *  Variante 4: Laden mittels FileUtils der Apache-Bibliothek org.apache.commons.io
   *-----------------------------------------------------------------------------*/

  void loadFile4()
  { 
    int overhead=0;
    /* nimm die Vorher-Zeit für Gesamtdurchlauf */   
    long completeTimeBefore = System.currentTimeMillis();       
 
    try
    {
      /* eigentliches Einlesen */
      /* readFileToString gibt es nicht direkt in Java, siehe auch
       * http://stackoverflow.com/questions/3849692/whole-text-file-to-a-string-in-java
       */

      File currentFile = new File(loadPath);
      completeString.append(FileUtils.readFileToString(currentFile, "utf-8"));
      currentFile = null;
    } catch (IOException e)
    {
      System.out.println("Datei konnte nicht eingelesen werden!");
      return;
    }            
    
    System.out.println("Dateigröße im RAM: " + (overhead=completeString.length()));   
    System.out.println("Overhead: " + (overhead-fileLength) + " Zeichen"); 
 
    /* nimm die Danach-Zeit für Gesamtdurchlauf und bestimme Differenz */   
    long completeTimeAfter = System.currentTimeMillis();     
    long completeTimeDiff   = completeTimeAfter - completeTimeBefore;       
    System.out.println(completeTimeDiff + " ms Zeit benötigt zum Datei-Laden.\n");     
  }

  public static void main(String[] args)
  {
    Program test = new Program();
    File file = new File(test.loadPath);
    test.fileLength = (int) file.length();  
    file = null
    System.out.println("Dateigröße auf Festplatte: " + test.fileLength); 
 
    //System.out.println("\nzeilenweises Einlesen: ");
    //test.loadFile1();

    //System.out.println("\nzeichenweises Einlesen: ");
    //test.loadFile2();

    //System.out.println("\nin 1000-Zeichen-Blöcken einlesen: ");
    //test.loadFile3();
    //test.test=test.completeString.toString();
 
    System.out.println("\nLaden mittels FileUtils der Apache-Bibliothek org.apache.commons.io: ");
    test.loadFile4();
 
     // Datei für Diff-Vergleich schreiben 
    /*try
    {
      FileWriter f1 = new FileWriter("C:/Users/thomas/Downloads/pg2600_2_2.txt");
      f1.write(test.completeString.toString());
      f1.close();
    } catch (IOException e) {
      System.out.println("Datei konnte nicht geschrieben werden!");
    } */

   
    System.out.println("Enter drücken um zu beenden.");
    try {
        int LV_IntAbort = System.in.read();
    } catch (IOException e) {
        System.out.println("FEHLER >>> " + e);
    }
  }
}

 

Monats-Liste