Input/Output Programm zum Auslesen/Beschreiben von Textdateien, wie Geschwindigkeit erhöhen?

BobDer

Mitglied
Guten Abend,
das Thema ist gewiss sehr speziell, aber vielleicht kann mir hier trotzdem geholfen werden.
Ich habe eine Textdatei, in der mehrere Hunderttausend binär codierte Werte mit einer Länge von je 4-9 Byte stehen. Diese möchte ich auslesen und in Abhängigkeit eines Wertes in eine von Acht Zieldateien schreiben.

Ein Grundprogramm mit der Funktionalität steht unten. Das Problem bei meinem Programm ist die lange Laufzeit. Je nach Umfang der Quelldatei schafft das Programm 5.000-6.000 Werte / Sekunde.

Leider konnte ich nicht genau lokalisieren, was genau so viel Zeit verbrät.
Vielleicht kann ja mal jemand einen Blick auf den Code werfen und mir Tipps zum Beschleunigen dse Vorgangs geben. Wäre echt super. 🙂

Main:
Java:
long startTime = System.currentTimeMillis();
int indexNumber[] = new int[8];
 
  try(FileInputStream fis = new FileInputStream(fileName);){
          int tempValue=0;
          int lastCode[] = {-1,-1,-1,-1,-1,-1,-1,-1};
         
         while(fis.available()>0){
              int tempCode=0;
              int tempChannel=0;
              int dataFragment=0;
              tempValue=fis.read();
              tempCode = tempValue>>6;
              tempChannel = (tempValue&0b00111100)>>2;
              dataFragment = (tempValue&0b00000011);
              array1[tempChannel][tempCode].setChannel(tempChannel);
              try(BufferedWriter bwriter = new BufferedWriter(new FileWriter(targetPath+"/Sensor"+tempChannel+".txt",true))){

              if(tempCode==0){ // Code 0
                  extractCode0(array1[tempChannel][0], fis, dataFragment);
                  indexNumber[tempChannel]++;
                  lastCode[tempChannel]=0;
                  bwriter.write(formatOutput(indexNumber[tempChannel], tempCode, tempChannel, array1[tempChannel][tempCode]));
                  bwriter.newLine();
              } else if (tempCode==1){ // Code 1
                  extractCode1(array1[tempChannel][1], fis, dataFragment);
                  completeDataCode1(array1[tempChannel][0], array1[tempChannel][1]);
                  indexNumber[tempChannel]++;
                  lastCode[tempChannel]=1;
                  bwriter.write(formatOutput(indexNumber[tempChannel], tempCode, tempChannel, array1[tempChannel][tempCode]));
                  bwriter.newLine();
              } else if (tempCode==2){ // Code 2
                  extractCode2(array1[tempChannel][2], fis, dataFragment);
                  indexNumber[tempChannel]++;
                  if(lastCode[tempChannel]==1){
                      completeDataCode2(array1[tempChannel][1], array1[tempChannel][2]);
                  } else if (lastCode[tempChannel]==0){
                      completeDataCode2(array1[tempChannel][0], array1[tempChannel][2]);
                  }
                  bwriter.write(formatOutput(indexNumber[tempChannel], tempCode, tempChannel, array1[tempChannel][tempCode]));
                  bwriter.newLine();
              }
              } catch (IOException e5){
                  System.err.println("Couldn't write to targetfiles.");
                  e5.printStackTrace();
              }
      
          }

  } catch (IOException   e4){
      System.err.println("Couldn't read sourcefile.");
      e4.printStackTrace();
  };
 
  long endTime = System.currentTimeMillis();
  int totalReading=0;
  for(int i=0;i<indexNumber.length;i++){
      totalReading+=indexNumber[i];
  }
  long elapsedTime = endTime-startTime;
  System.out.println("Read "+totalReading+" values in "+elapsedTime+ " Milliseconds.");

Funktionen unter der Main:
Java:
static void extractCode0(Messwert messwert, FileInputStream fis, int dataFragment) throws IOException{
        int valueArray[] = new int[8];
        int frequenz=0;
        switch(dataFragment){
            case 0:
                frequenz=1;
                break;
            case 1:
                frequenz=10;
                break;
            case 2:
                frequenz=100;
                break;
            case 3:
                frequenz=1000;
                break;
        }
        messwert.setFreq(frequenz);
       
        for(int i=0;i<valueArray.length;i++){
            valueArray[i]=fis.read();
        }
       
        int day = valueArray[0]>>3;
        messwert.setDay(day);
       
        int month = ((valueArray[0]&0b00000111)<<1)|(valueArray[1]>>7);
        messwert.setMonth(month);
       
        int year = valueArray[1]&0b01111111;
        messwert.setYear(year);
       
        int hour = valueArray[2]>>3;
        messwert.setHour(hour);
       
        int minute = ((valueArray[2]&0b00000111)<<3)|(valueArray[3]>>5);
        messwert.setMinute(minute);
       
        int second = ((valueArray[3]&0b00011111)<<1)|(valueArray[4]>>7);
        messwert.setSecond(second);
       
        int millisecond = ((valueArray[4]&0b01111111)<<3)|(valueArray[5]>>5);
        messwert.setMillisecond(millisecond);
       
        int measurement = ((valueArray[5]&0b00011111)<<10)|(valueArray[6]<<2)|(valueArray[7]>>6);

        messwert.setMesswert(measurement/1000.0);
    }

Java:
static void extractCode1(Messwert messwert, FileInputStream fis, int dataFragment) throws IOException{
       
        
        int valueArray[] = new int[4];
       
        for(int i=0;i<valueArray.length;i++){
            valueArray[i]=fis.read();
        }
       
        int second = dataFragment<<4|(valueArray[0]>>4);
        messwert.setSecond(second);
       
        int millisecond = ((valueArray[0]&0b00001111)<<6)|(valueArray[1]>>2);
        messwert.setMillisecond(millisecond);
       
        int measurement = ((valueArray[1]&0b00000011)<<13)|(valueArray[2]<<5)|(valueArray[3]>>3);

        messwert.setMesswert(measurement/1000.0);
       
    }
Java:
  static void extractCode2(Messwert messwert, FileInputStream fis, int dataFragment) throws IOException{
       

        int valueArray[] = new int[3];
     
        for(int i=0;i<valueArray.length;i++){
            valueArray[i]=fis.read();
        }
               
        int millisecond = dataFragment<<8|valueArray[0];
        messwert.setMillisecond(millisecond);
       
        int measurement = (valueArray[1]<<7)|(valueArray[2]>>1);
       
        messwert.setMesswert(measurement/1000.0);
       
    }
Java:
  static void completeDataCode1(Messwert dataSource, Messwert dataSink){
        dataSink.setFreq(dataSource.getFreq());
        dataSink.setDay(dataSource.getDay());
        dataSink.setMonth(dataSource.getMonth());
        dataSink.setYear(dataSource.getYear());
        dataSink.setHour(dataSource.getHour());
        dataSink.setMinute(dataSource.getMinute());
    }
Java:
static void completeDataCode2(Messwert dataSource, Messwert dataSink){
        dataSink.setFreq(dataSource.getFreq());
        dataSink.setDay(dataSource.getDay());
        dataSink.setMonth(dataSource.getMonth());
        dataSink.setYear(dataSource.getYear());
        dataSink.setHour(dataSource.getHour());
        dataSink.setMinute(dataSource.getMinute());
        dataSink.setSecond(dataSource.getSecond());
    }
Java:
static String formatOutput(int index, int code, int channel, Messwert data){
       
        char unit;
        if(channel<7){
            unit = 'V';
        } else {
            unit = 'A';
        }
        // Format: index Code Channel Freq DD.MM.YY HH:MM:SS.ms Value
        String format = "%d. C%d Kanal %d %dHz %02d.%02d.%02d %02d:%02d:%02d.%03d %.3f%c";                     
        String output = String.format(format, index, code, channel,data.getFreq(),data.getDay(),data.getMonth(),data.getYear(),data.getHour(),data.getMinute(),data.getSecond(),data.getMillisecond(),data.getMesswert(),unit);
        return output;

    }
 
Danke für die Antwort.
Ich bin eher ein Anfänger. Als IDE benutze ich Eclipse 4.5.1.

Einen Profiler habe ich noch nie beutzt. Ich werde mal gucken was das ist und wie man es benutzt.

Ich habe spontan keinen vernünftigen Weg gefunden, wie man den Writer sonst dynamisch auf die entsprechende Datei setzen kann. Alternativ könnte man vor der Schleife ein halbes Dutzend Writer auf einmal öffnen und dann in der Schleife auf den entsprechenden Writer zu der gewünschten Datei zugreifen.
 
Also ich würde die Werte erstmal auslesen und in 8 Strings speichern und dann wenn die String erstellt sind mit einem mal schreiben.

Gruß

Claus
 
ich würde switch verwenden

if(tempCode==0){ // Code 0
extractCode0(array1[tempChannel][0], fis, dataFragment);
indexNumber[tempChannel]++;
lastCode[tempChannel]=0;
bwriter.write(formatOutput(indexNumber[tempChannel], tempCode, tempChannel, array1[tempChannel][tempCode]));
bwriter.newLine();
} else if (tempCode==1){ // Code 1
extractCode1(array1[tempChannel][1], fis, dataFragment);
completeDataCode1(array1[tempChannel][0], array1[tempChannel][1]);
indexNumber[tempChannel]++;
lastCode[tempChannel]=1;
bwriter.write(formatOutput(indexNumber[tempChannel], tempCode, tempChannel, array1[tempChannel][tempCode]));
bwriter.newLine();
} else if (tempCode==2){ // Code 2
extractCode2(array1[tempChannel][2], fis, dataFragment);
indexNumber[tempChannel]++;
if(lastCode[tempChannel]==1){
completeDataCode2(array1[tempChannel][1], array1[tempChannel][2]);
} else if (lastCode[tempChannel]==0){
completeDataCode2(array1[tempChannel][0], array1[tempChannel][2]);
}
bwriter.write(formatOutput(indexNumber[tempChannel], tempCode, tempChannel, array1[tempChannel][tempCode]));
bwriter.newLine();
}
 
weil es schneller ist...

Jetzt wirst du sagen: wer sagt das? Dann werde ich sagen: weil switch nunmal schneller ist..und ich es schon 1-2-3-viele schon bei solchen Zwecken verwendet habe. (Excel)
Dann wirst du sagen: wo steht das?
Dann werde ich sagen: ich bin dann mal weg! Schönes Wochenende an den TE

😉
 
weil es schneller ist...

Jetzt wirst du sagen: wer sagt das? Dann werde ich sagen: weil switch nunmal schneller ist..und ich es schon 1-2-3-viele schon bei solchen Zwecken verwendet habe. (Excel)
Dann wirst du sagen: wo steht das?
Dann werde ich sagen: ich bin dann mal weg! Schönes Wochenende an den TE

😉

Eine Empfehlung zu Performance geben und die nicht mal begründen können ist immer super...
Vor allem wenn das eine absolute Micro-Optimization ist, die, selbst wenn es Unterschiede zwischen den beiden geben würde, auf die Gesamt-Performance nur minimalste Auswirkungen hätte...


In diesem Fall führt beides zu annähernd dem gleichen Assembler-Code (der eine hat ein je, der andere ein jne), dürfte deshalb sogar eher pro-if ausgehen, weil's damit seltener zum Sprung kommt. Aber selbst das dürfte mit Branch-Prediction völlig irrelevant sein...
 
Guten Abend,
ich habe das Programm jetzt mit "JProfiler" ein wenig unter die Lupe genommen.

Den größten Einfluss hatte der ständige Wechsel des BufferedWriter innerhalb der Schleife.
Daher habe ich jetzt vor der Schleife ein Array mit Writern angelegt, die auf die entsprechende Ausgangsdatei zeigen und in der Schleife nur ausgewählt werden.
Java:
BufferedWriter bwriterA[] = new BufferedWriter[8];
          try{
              for(int i=0;i<bwriterA.length;i++){
                  bwriterA[i]=new BufferedWriter(new FileWriter(targetPath+"/Sensor"+(i+1)+".txt"));
              }

           // Wie im ersten Beitrag

           }catch(IOException e5){
              for(int i=0;i<bwriterA.length;i++){
                  bwriterA[i].close();
              }
              System.err.println("Couldn't write to targetfiles.");
              e5.printStackTrace();
          } finally {
              for(int i=0;i<bwriterA.length;i++){
                  bwriterA[i].close();
              }
          }
Dafür bin ich wieder auf try{}catch{}finally{} umgestiegen, da man in dem try() mit auto-close wohl kein Array anlegen kann. Zumindest habe ich es nicht hinbekommen.

Alleine dadurch ist die Laufzeit (gemessen über die Systemzeit) bei 2,3 Millionen Datensätzen von ca. 388 Sekunden auf 60,5 Sekunden gesunken.
Ein weiterer Zeitfresser war String.format in formatOutput.
Damit hat sich zwar jetzt die Formatierung von
1. C0 Kanal 1 1000Hz 22.03.18 20:39:50.051 0,439V nach
1. C0 Kanal 1 1000Hz 22.3.18 20:39:50.51 0.439V ein wenig geändert, dafür ist die Zeit auf knapp über 30 Sekunden gesunken.
Einziger Nachteil ist jetzt die Darstellung von "00" Werten, die nur noch als "0" dargestellt werden. Gibt es da eine gute Alternative zum Stringformat, um vorstehende Nullen darzustellen?

Die Laufzeit teilt sich jetzt so auf:
performance4.JPG
Die 10.523 ms in java.io.FileInputStream.available, ist das die Zeit, die das Programm nur in der while(fis.available()>0)-Schleife, aber außerhalb von den anderen Funktionen verbringt?
Java:
while(fis.available()>0){
             
              int tempCode=0;
              int tempChannel=0;
              int dataFragment=0;
             
              tempValue=fis.read();
             
              tempCode = tempValue>>6;
              tempChannel = ((tempValue&0b00111100)>>2)-1;
              dataFragment = (tempValue&0b00000011);
             
              array1[tempChannel][tempCode].setChannel(tempChannel);
              if(tempCode==0){ // Code 0
                  extractCode0(array1[tempChannel][0], fis, dataFragment);
                  indexNumber[tempChannel]++;
                  lastCode[tempChannel]=0;
                  bwriterA[tempChannel].write(formatOutput(indexNumber[tempChannel], tempCode, tempChannel, array1[tempChannel][tempCode]));
                  bwriterA[tempChannel].newLine();

              } else if (tempCode==1){ // Code 1
                  extractCode1(array1[tempChannel][1], fis, dataFragment);
                  completeDataCode1(array1[tempChannel][0], array1[tempChannel][1]);
                  indexNumber[tempChannel]++;
                  lastCode[tempChannel]=1;
                  bwriterA[tempChannel].write(formatOutput(indexNumber[tempChannel], tempCode, tempChannel, array1[tempChannel][tempCode]));
                  bwriterA[tempChannel].newLine();
                 
              } else if (tempCode==2){ // Code 2
                  extractCode2(array1[tempChannel][2], fis, dataFragment);
                  indexNumber[tempChannel]++;
                  if(lastCode[tempChannel]==1){
                      completeDataCode2(array1[tempChannel][1], array1[tempChannel][2]);
                  } else if (lastCode[tempChannel]==0){
                      completeDataCode2(array1[tempChannel][0], array1[tempChannel][2]);
                  }
                  bwriterA[tempChannel].write(formatOutput(indexNumber[tempChannel], tempCode, tempChannel, array1[tempChannel][tempCode]));
                  bwriterA[tempChannel].newLine();
              }
          }
 
Dafür bin ich wieder auf try{}catch{}finally{} umgestiegen, da man in dem try() mit auto-close wohl kein Array anlegen kann. Zumindest habe ich es nicht hinbekommen.
try-with-resources funktioniert nicht mit Arrays. Wenn man da so was dynamisches will, muss man ein bisschen basteln und kann damit das schließen zumindest etwas "verstecken"

Ein weiterer Zeitfresser war String.format in formatOutput.
Damit hat sich zwar jetzt die Formatierung von
1. C0 Kanal 1 1000Hz 22.03.18 20:39:50.051 0,439V nach
1. C0 Kanal 1 1000Hz 22.3.18 20:39:50.51 0.439V ein wenig geändert, dafür ist die Zeit auf knapp über 30 Sekunden gesunken.
Einziger Nachteil ist jetzt die Darstellung von "00" Werten, die nur noch als "0" dargestellt werden. Gibt es da eine gute Alternative zum Stringformat, um vorstehende Nullen darzustellen?
Wie sieht der Code dazu aus?
Möglicherweise reicht schon sowas in der Art von x<100 ? "0"+x : x


Die 10.523 ms in java.io.FileInputStream.available, ist das die Zeit, die das Programm nur in der while(fis.available()>0)-Schleife, aber außerhalb von den anderen Funktionen verbringt?
Die es nur in fis.available() verbringt - darauf hast du aber nicht wirklich Einfluss.

uU kann man das verbessern, indem man die Datei in einem Rutsch einliest und danach mit einem byte-Array arbeitet, oder aber das ganze in einen BufferedInputStream wrappt. Ich weiß allerdings nicht, wie groß die Auswirkungen in diesem Fall sind
 
Zuletzt bearbeitet:
Du liest jedes Byte einzeln ein. Das ist sehr langsam. Versuch statt dessen eine grössere Anzahl an Bytes gleichzeitig in ein Array zu lesen, z.B. 1 Million. Die Methode FileInputStream.read(byte[] array) liefert die Anzahl der gelesenen Bytes zurück. Wenn das Ende der Datei erreicht ist, wird -1 zurückgeliefert. Die available() Methode brauchst du dann also gar nicht mehr.
 
So, ich habe jetzt noch ein wenig rumprobiert.
Durch den Wechsel von einem FileInputStream zu einem BufferedInputStream ist die Zeit von ca. 33 Sekunden auf ca. 11 Sekunden gesunken.

Danach habe ich versucht einen FileInputStream mit read(byte[] bytearray) zu erstellen.
Das hat am Ende auch geklappt, allerdings war das Resultat ernüchternd. Je nach Buffergröße lag die Zeit bei ca. 10-11 Sekunden, Zeitersparnis ist bei meiner Implementierung nahezu nicht existent.

Implementierung:

Java:
package hauptpaket;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class Fileaccess {
    private  byte bytebuffer[] = new byte[8192];
    private  int position=0;
    private  int remainderCount=0;
    private  FileInputStream fisObject;

   public  void reset(){
       this.position=0;
    }
public  int getRemainderData(){
        return this.remainderCount;
    }

public  void linkFIS(FileInputStream fisObjectIn){
        this.fisObject=fisObjectIn;
    }

public  int read(){
        int value=-1;
        if(this.position<this.bytebuffer.length){
            value=this.bytebuffer[this.position]&0xff;
            this.position++;
            if(this.remainderCount>0){
                this.remainderCount--;
            }
        }
        return value;
    }


public int readData() throws IOException{
        int value=-1;
       
        if(this.remainderCount==0){   
            this.remainderCount=fisObject.read(bytebuffer);
            reset();
        }
        value=read();
        return value;
    }


Als kleines rudimentäres "Experiment" hatte ich dann noch BufferedInputStream mit einem ByteArrayInputStream verglichen.
Java:
System.out.println("Test BufferedInputStream:");
        long testTime1 = System.currentTimeMillis();
        try(BufferedInputStream bis1 = new BufferedInputStream(new FileInputStream("test.txt"));){
byte bytebuffer1[] = new byte[bis1.available()];
int index=0;
while(bis1.available()>0){
                bytebuffer1[index]=(byte)bis1.read();
                index++;
            }

try(BufferedWriter bwriter1 = new BufferedWriter(new FileWriter("testout1.txt"))){
char c;
               
                for(byte b:bytebuffer1){
                    c = (char)b;
                    bwriter1.write(c);
                   
                }
                }catch(IOException e8){
                   
                }
} catch(IOException e7){
            System.err.println("Fehler");
        }
long testTime2 = System.currentTimeMillis();
        System.out.println("Zeit: "+(testTime2-testTime1)+"ms");

Java:
System.out.println("Test ByteArrayInputStream:");
        byte bytebuffer[] = new byte[8192];
        try(FileInputStream fis1 = new FileInputStream("test.txt");){
int readCount=0;
char c;
            ByteArrayInputStream bais = new ByteArrayInputStream(bytebuffer);
            try(BufferedWriter bwriter1 = new BufferedWriter(new FileWriter("testout2.txt"))){
               
               
                while((readCount=fis1.read(bytebuffer))!=-1){
int baisCount=0;
int preCount=bytebuffer.length;
while((baisCount=bais.available())>(preCount-readCount)){
                       
                        bwriter1.write((char)bais.read());
                    }
                    bais.reset();
                   
                }
}catch(IOException e8){
               
            }
           
           
        } catch(IOException e7){
            System.err.println("Fehler");
        }
        long testTime3 = System.currentTimeMillis();
        System.out.println("Zeit: "+(testTime3-testTime2)+"ms");

Dabei waren in der Inputdatei einfach eine lange Zeichenfolge.
Das Überraschende:
Für die Datei mit 2.237.761 Zeichen hat der BIS ca. 5 Sekunden gebraucht.
Der BAIS war, zumindest bei dem Test oben, nach <100ms fertig.
Die beiden Outputdateien sind identisch.
Habe ich bei den Testprogrammen etwas falsch gemacht oder ist das BAIS wirklich so viel schneller? Nur *spoiler* profitiert das Hauptprogramm von diesem vermeintlichen Geschwindigkeitsvorteil nicht.

Ich hatte dann mal versucht das im Hauptprogramm umzusetzen:

Java:
package hauptpaket;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class Fileaccess {
    private  byte bytebuffer[] = new byte[8192];
    private  int position=0;
    private  int remainderCount=0;
    private  FileInputStream fisObject;
    private ByteArrayInputStream baisObject;
  
    public  void reset(){
        baisObject.reset();
    }
   
   
   
    public  void setRemainderData(int count){
        this.remainderCount=count;
       
    }
   
    public  int getRemainderData(){
        return this.remainderCount;
    }
   
    public  void linkFIS(FileInputStream fisObjectIn){
        this.fisObject=fisObjectIn;
    }
   
    public  void createBAIS(){
        this.baisObject= new ByteArrayInputStream(this.bytebuffer);
    }
      
    public  int read(){
        int value=-1;

        if(remainderCount>0){
            value=baisObject.read()&0xff;
            if(this.remainderCount>0){
                this.remainderCount--;
            }
        }

        return value;
    }
   
    public int readData() throws IOException{
        int value=-1;
       
        if(this.remainderCount==0){
              this.remainderCount=fisObject.read(bytebuffer);
            reset();
        }
  
       
        value=read();
  
        return value;
    }
   
}
In der Main dann:
Java:
try(FileInputStream fis = new FileInputStream(fileName);){
Fileaccess saveHelp = new Fileaccess();
saveHelp.linkFIS(fis);
          saveHelp.createBAIS();
 [....]
while(fis.available()>0||saveHelp.getRemainderData()>0){
[....]
// Aufruf mit
tempValue=saveHelp.readData();
[....]

Leider liegt die Zeitersparnis in meinem Fall, gegenüber dem einfachen BufferedInputStream, bei +- 0.

Den ganzen Aufwand hätte ich mir dann wohl sparen können. 😉
Das Einlesen der Daten scheint dann wohl doch nicht (mehr) der Flaschenhals zu sein.

So fällt mir dazu aktuell aber auch nichts mehr ein, vielleicht bekomme ich das Programm in dem Anwendungsfall einfach nicht mehr schneller und werde damit leben müssen.
 
Das Einlesen der Daten scheint dann wohl doch nicht (mehr) der Flaschenhals zu sein.
Das kannst du einfach mit‘m Profiler überprüfen 😉


Die beiden Programme muss ich mir mal aufm Rechner angucken, ich würde da zumindest kleine Unterschiede vermuten.
Aufm Handy stört da aber die Formatierung zu viel...
 
weil es schneller ist...

Jetzt wirst du sagen: wer sagt das? Dann werde ich sagen: weil switch nunmal schneller ist..und ich es schon 1-2-3-viele schon bei solchen Zwecken verwendet habe. (Excel)
Dann wirst du sagen: wo steht das?
Dann werde ich sagen: ich bin dann mal weg! Schönes Wochenende an den TE

😉
Code:
A switch compiles into a lookup table which is fundamentally different then the if/else statements. If the last statement in a if/else chain is the one you want you have to check all the previous if/else first. This is not the case with a switch.
Code:
The power of switch is twofold:
[LIST=1]
[*]It is more descriptive
[*]It saves CPU cycles
[/LIST]
Your post already comments on the descriptiveness of switch compared to a chain of ifs, so I will concentrate on part 2.

If the number of switch labels is N, a chain of ifs takes O(N) to dispatch, while a switch always dispatches in O(1). This is how it is done: compiler places addresses of switch labels into an array, computes an index based on the label values, and performs a jump directly, without comparing the switch expression to value one by one. In case of strings used as switch labels, the compiler produces a hash table, which looks up an address in O(1) as well.

When the number of switch becomes high (above 100), converting between switch and a chain of ifs could no longer be regarded as a micro-optimization: time savings may be very significant, especially in tight loops. If a profiler tells you that your chain of ifs is taking a significant time, rewriting as a switch may very well be a solution.

Quelle: https://softwareengineering.stackex...atement-better-than-a-series-of-if-statements

Heißt für mich das das kleinere if-else Ketten (1 - 10) geordnet von oben nach unten nach dem Fall der am wahrscheinlichsten Eintreten wird schneller sind als ein switch. Bei größeren wäre jedoch ein switch besser.
 
Code:
A switch compiles into a lookup table which is fundamentally different then the if/else statements. If the last statement in a if/else chain is the one you want you have to check all the previous if/else first. This is not the case with a switch.
Code:
The power of switch is twofold:
[LIST=1]
[*]It is more descriptive
[*]It saves CPU cycles
[/LIST]
Your post already comments on the descriptiveness of switch compared to a chain of ifs, so I will concentrate on part 2.

If the number of switch labels is N, a chain of ifs takes O(N) to dispatch, while a switch always dispatches in O(1). This is how it is done: compiler places addresses of switch labels into an array, computes an index based on the label values, and performs a jump directly, without comparing the switch expression to value one by one. In case of strings used as switch labels, the compiler produces a hash table, which looks up an address in O(1) as well.

When the number of switch becomes high (above 100), converting between switch and a chain of ifs could no longer be regarded as a micro-optimization: time savings may be very significant, especially in tight loops. If a profiler tells you that your chain of ifs is taking a significant time, rewriting as a switch may very well be a solution.

Quelle: https://softwareengineering.stackex...atement-better-than-a-series-of-if-statements

Heißt für mich das das kleinere if-else Ketten (1 - 10) geordnet von oben nach unten nach dem Fall der am wahrscheinlichsten Eintreten wird schneller sind als ein switch. Bei größeren wäre jedoch ein switch besser.
wobei man in dem Fall nicht den JIT-Compiler außer Acht lassen sollte - manches an solchen Optimierungen, was für "normale" Compiler gilt, gilt für die nicht mehr unbedingt.
Theoretisch ließen sich solche if-kaskaden auch genauso optimierten, wie der entsprechende switch (und ich hoffe ganz stark, dass jemand in naher Zukunft eine entsprechende Optimierung für Graal schreibt 😉)
 
Dabei waren in der Inputdatei einfach eine lange Zeichenfolge.
Das Überraschende:
Für die Datei mit 2.237.761 Zeichen hat der BIS ca. 5 Sekunden gebraucht.
Der BAIS war, zumindest bei dem Test oben, nach <100ms fertig.
Die beiden Outputdateien sind identisch.
Habe ich bei den Testprogrammen etwas falsch gemacht oder ist das BAIS wirklich so viel schneller? Nur *spoiler* profitiert das Hauptprogramm von diesem vermeintlichen Geschwindigkeitsvorteil nicht.

Das erste Problem dabei dürfte sein, dass die beiden direkt nacheinander laufen.
Während das erste läuft, dürften der Großteil der Optimierungen durchlaufen (also noch langsamer Code und zusätzlich der laufende Compiler), der zweite nutzt dann den optimierten Code (also schneller Code und kein Compiler).

Dazu sind beide Programme nicht das gelbe vom Ei.
In BIS liest du die Datei ein und überträgst die *gesamte* Datei dann Byte-weise in ein einzelnes Array, um dieses dann wieder Byte-Weise zu schreiben.

In BAIS überträgst du die Datei erst in einen Buffer (das, was bei BIS im Hintergrund passiert), und liest das dann über den ByteArrayStream wieder byte-weise aus.

Sinnvoll wäre eine Mischung: Blockweise einlesen (BIS oder direkt in Array) und dann daraus direkt schreiben (entweder wieder BIS oder übers Array iterieren).
 
Dabei waren in der Inputdatei einfach eine lange Zeichenfolge.
Das Überraschende:
Für die Datei mit 2.237.761 Zeichen hat der BIS ca. 5 Sekunden gebraucht.
Der BAIS war, zumindest bei dem Test oben, nach <100ms fertig.
Die beiden Outputdateien sind identisch.
Habe ich bei den Testprogrammen etwas falsch gemacht oder ist das BAIS wirklich so viel schneller? Nur *spoiler* profitiert das Hauptprogramm von diesem vermeintlichen Geschwindigkeitsvorteil nicht.

Das erste Problem dabei dürfte sein, dass die beiden direkt nacheinander laufen.
Während das erste läuft, dürften der Großteil der Optimierungen durchlaufen (also noch langsamer Code und zusätzlich der laufende Compiler), der zweite nutzt dann den optimierten Code (also schneller Code und kein Compiler).

Dazu sind beide Programme nicht das gelbe vom Ei.
In BIS liest du die Datei ein und überträgst die *gesamte* Datei dann Byte-weise in ein einzelnes Array, um dieses dann wieder Byte-Weise zu schreiben.

In BAIS überträgst du die Datei erst in einen Buffer (das, was bei BIS im Hintergrund passiert), und liest das dann über den ByteArrayStream wieder byte-weise aus.

Sinnvoll wäre eine Mischung: Blockweise einlesen (BIS oder direkt in Array) und dann daraus direkt schreiben (entweder wieder BIS oder übers Array iterieren).
 
...
Das Überraschende:
Für die Datei mit 2.237.761 Zeichen hat der BIS ca. 5 Sekunden gebraucht.
Der BAIS war, zumindest bei dem Test oben, nach <100ms fertig.
Die beiden Outputdateien sind identisch.
Habe ich bei den Testprogrammen etwas falsch gemacht oder ist das BAIS wirklich so viel schneller? Nur *spoiler* profitiert das Hauptprogramm von diesem vermeintlichen Geschwindigkeitsvorteil nicht.

Das Problem ist, dass du die available Methode ständig aufrufst, denn die ist relativ langsam. Und du brauchst sie auch nicht. Am besten entfernst du sämtliche Aufrufe von available in deinem gesamten Programm. Dann wird das ganze wahrscheinlich sehr viel schneller laufen.

Ich habe dein BIS Beispiel etwas abgeändert. Jetzt braucht es nur noch 45ms für 2,2 MB und 1,7s für 200MB.
Java:
long testTime1 = System.currentTimeMillis();
try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream("test.txt"))) {
  try(BufferedWriter bwriter = new BufferedWriter(new FileWriter("testout1.txt"))) {
    int bytesRead;
    byte[] arr = new byte[1024*1024];
    while((bytesRead = bis.read(arr)) >= 0) {
      for(int i = 0; i < bytesRead; i++) {
        bwriter.write((char)arr[i]);
      }
    }
  } catch(IOException e1) {
    e1.printStackTrace();
  }
} catch(IOException e2) {
    e2.printStackTrace();
}
long testTime2 = System.currentTimeMillis();
System.out.println("Zeit: "+(testTime2-testTime1)+"ms");
 

Zurück
Oben