Почему использование BufferedInputStream для чтения файла байта за байтом быстрее, чем с помощью FileInputStream?

Я пытался прочитать файл в массив с помощью FileInputStream, и файл ~800 КБ занял около 3 секунд для чтения в память. Затем я попробовал тот же код, за исключением того, что FileInputStream заключен в BufferedInputStream, и это заняло около 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 раз быстрее. Гораздо больше, чем это. Итак, почему это так, и возможно ли сделать этот код более эффективным (без использования каких-либо внешних библиотек)?

3 ответа

Решение

В FileInputStream, метод read() читает один байт. Из исходного кода:

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

Это собственный вызов ОС, который использует диск для чтения одного байта. Это тяжелая операция.

С BufferedInputStream, метод делегирует перегруженному read() метод, который читает 8192 количество байтов и буферизует их, пока они не понадобятся. Он по-прежнему возвращает только один байт (но сохраняет остальные в резерве). Таким образом, BufferedInputStream делает меньше собственных вызовов ОС для чтения из файла.

Например, ваш файл 32768 длинные байты. Чтобы получить все байты в памяти с FileInputStream, вам потребуется 32768 родные звонки в ОС. С BufferedInputStream, вам потребуется только 4независимо от количества read() звонки ты будешь делать (еще 32768).

Что касается того, как сделать это быстрее, вы можете рассмотреть NIO в Java 7 FileChannel класс, но у меня нет никаких доказательств в поддержку этого.

BufferedInputStream, обернутый вокруг FileInputStream, будет запрашивать данные из FileInputStream большими кусками (по-моему, 512 байт или около того). Таким образом, если вы читаете 1000 символов по одному, FileInputStream придется только дважды перейти на диск, Это будет намного быстрее!

Это из-за стоимости доступа к диску. Предположим, у вас будет файл размером 8 КБ. Для чтения этого файла без BufferedInputStream потребуется диск доступа 8*1024 раза.

На этом этапе BufferedStream выходит на сцену и действует как посредник между FileInputStream и файлом, который нужно прочитать.

В одном кадре получит куски байтов по умолчанию 8 КБ в память, а затем 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));
    }
Другие вопросы по тегам