web-dev-qa-db-ja.com

FileedStreamを使用するよりもBufferedInputStreamを使用してファイルをバイト単位で高速に読み取るのはなぜですか?

FileInputStreamを使用してファイルを配列に読み込もうとしており、約800 KBのファイルをメモリに読み込むのに約3秒かかりました。次に、BufferedInputStreamにラップされたFileInputStreamを除いて同じコードを試しましたが、約76ミリ秒かかりました。バイト単位でファイルを読み取るのに、BufferedInputStreamを使用すると、バイト単位でファイルを読み取る速度が非常に速くなるのはなぜですか?コードは次のとおりです(残りのコードは完全に無関係です)。これは「高速」コードであることに注意してください。 「遅い」コードが必要な場合は、BufferedInputStreamを削除できます。

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は30倍以上高速です。それよりもはるかに。だから、これはなぜですか、このコードをより効率的にすることは可能ですか(外部ライブラリを使用せずに)?

58
ZimZim

FileInputStreamでは、メソッドread()は1バイトを読み取ります。ソースコードから:

_/**
 * 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;
_

これは、ディスクを使用して1バイトを読み取るOSのネイティブ呼び出しです。これは重い操作です。

BufferedInputStreamを使用すると、メソッドは_8192_バイトの量を読み取り、必要になるまでバッファリングするオーバーロードされたread()メソッドに委任します。まだ1バイトのみを返します(ただし、他のバイトは予約されています)。このようにして、BufferedInputStreamは、ファイルから読み取るためのOSへのネイティブ呼び出しを少なくします。

たとえば、ファイルの長さは_32768_バイトです。 FileInputStreamを使用してメモリ内のすべてのバイトを取得するには、OSへの_32768_ネイティブ呼び出しが必要になります。 BufferedInputStreamを使用すると、read()呼び出しの回数に関係なく、_4_のみが必要になります(_32768_)。

より高速にする方法については、Java 7のNIO FileChannelクラスを検討することをお勧めしますが、これをサポートする証拠はありません。

106

FileInputStreamにラップされたBufferedInputStreamは、大きなチャンク(デフォルトでは512バイト程度)でFileInputStreamにデータを要求します。したがって、一度に1000文字を読み取る場合、FileInputStreamはディスクに2回アクセスするだけで済みます。 。これははるかに高速です!

2
usha

これは、ディスクアクセスのコストが原因です。サイズが8kbのファイルがあると仮定しましょう。 BufferedInputStreamなしでこのファイルを読み取るには、8 * 1024回アクセスディスクが必要です。

この時点で、BufferedStreamはシーンに到達し、FileInputStreamと読み取るファイルの間の仲介者として機能します。

ワンショットで、バイトのチャンクを取得します。デフォルトはメモリに8kbで、FileInputStreamはこの中間者からバイトを読み取ります。これにより、操作の時間が短縮されます。

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