it-swarm.com.de

Warum ist die Verwendung von BufferedInputStream zum Lesen einer Datei Byte für Byte schneller als mit FileInputStream?

Ich habe versucht, eine Datei mit FileInputStream in ein Array zu lesen, und eine ~ 800KB-Datei benötigte etwa 3 Sekunden, um in den Speicher zu lesen. Ich habe dann den gleichen Code ausprobiert, es sei denn, der FileInputStream wurde in einen BufferedInputStream eingebunden und es dauerte etwa 76 Millisekunden. Warum wird das Lesen einer Datei Byte für Byte mit einem BufferedInputStream so viel schneller ausgeführt, obwohl ich sie immer noch Byte für Byte lese? Hier ist der Code (der Rest des Codes ist völlig irrelevant). Beachten Sie, dass dies der "schnelle" Code ist. Sie können den BufferedInputStream einfach entfernen, wenn Sie den "langsamen" Code wünschen: 

InputStream is = null;

    try {
        is = new BufferedInputStream(new FileInputStream(file));

        int[] fileArr = new int[(int) file.length()];

        for (int i = 0, temp = 0; (temp = is.read()) != -1; i++) {
            fileArr[i] = temp;
        }

BufferedInputStream ist über 30-mal schneller. Weit mehr als das. Warum also, und ist es möglich, diesen Code effizienter zu gestalten (ohne externe Bibliotheken zu verwenden)?

54
ZimZim

In FileInputStream liest die Methode read() ein einzelnes Byte. Aus dem Quellcode:

/**
 * Reads a byte of data from this input stream. This method blocks
 * if no input is yet available.
 *
 * @return     the next byte of data, or <code>-1</code> if the end of the
 *             file is reached.
 * @exception  IOException  if an I/O error occurs.
 */
public native int read() throws IOException;

Dies ist ein systemeigener Aufruf an das Betriebssystem, das die Festplatte zum Lesen des einzelnen Bytes verwendet. Dies ist eine schwere Operation.

Mit einer BufferedInputStream delegiert die Methode an eine überladene read()-Methode, die die 8192-Menge an Bytes liest und sie puffert, bis sie benötigt werden. Es wird immer nur das einzelne Byte zurückgegeben (die anderen bleiben jedoch in Reserve). Auf diese Weise führt BufferedInputStream weniger native Aufrufe an das Betriebssystem aus, um aus der Datei zu lesen.

Beispielsweise ist Ihre Datei 32768 Bytes lang. Um alle Bytes mit einer FileInputStream im Speicher abrufen zu können, benötigen Sie 32768 native Aufrufe des Betriebssystems. Mit einer BufferedInputStream benötigen Sie nur 4, unabhängig von der Anzahl der read()-Aufrufe, die Sie ausführen werden (immer noch 32768).

Um es schneller zu machen, möchten Sie vielleicht die NIO-Klasse FileChannel von Java 7 in Betracht ziehen, aber ich habe keine Beweise, um dies zu unterstützen.

99

Ein um einen FileInputStream gewickelter BufferedInputStream fordert Daten vom FileInputStream in großen Abschnitten an (standardmäßig etwa 512 Byte, denke ich.) Wenn Sie also 1000 Zeichen einzeln lesen, muss der FileInputStream nur zweimal auf die Festplatte gehen . Das geht viel schneller!

2
usha

Dies liegt an den Kosten des Festplattenzugriffs. Nehmen wir an, Sie haben eine Datei mit einer Größe von 8kb. Zum Lesen dieser Datei ohne BufferedInputStream wird eine Zugriffszeit von 8 * 1024 Mal benötigt. 

An diesem Punkt tritt BufferedStream in die Szene ein und fungiert als Mittler zwischen FileInputStream und der zu lesenden Datei.

In einer Aufnahme werden 8 Bytes in den Speicher geladen, und FileInputStream liest die Bytes von diesem mittleren Mann. Dies verringert die Zeit der Operation. 

private void exercise1WithBufferedStream() {
      long start= System.currentTimeMillis();
        try (FileInputStream myFile = new FileInputStream("anyFile.txt")) {
            BufferedInputStream bufferedInputStream = new BufferedInputStream(myFile);
            boolean eof = false;
            while (!eof) {
                int inByteValue = bufferedInputStream.read();
                if (inByteValue == -1) eof = true;
            }
        } catch (IOException e) {
            System.out.println("Could not read the stream...");
            e.printStackTrace();
        }
        System.out.println("time passed with buffered:" + (System.currentTimeMillis()-start));
    }


    private void exercise1() {
        long start= System.currentTimeMillis();
        try (FileInputStream myFile = new FileInputStream("anyFile.txt")) {
            boolean eof = false;
            while (!eof) {
                int inByteValue = myFile.read();
                if (inByteValue == -1) eof = true;
            }
        } catch (IOException e) {
            System.out.println("Could not read the stream...");
            e.printStackTrace();
        }
        System.out.println("time passed without buffered:" + (System.currentTimeMillis()-start));
    }
0
huseyin