2017-02-04 9 views
0

Недавно я создал интерфейс, который заставляет пользователя реализовать один fromStream(OutputStream) со своими методами по умолчанию, глядя, как это:Что делает чтение из файлов без буферов настолько дорогостоящим?

public default T fromFile(File file) throws IOException { 
    try (InputStream stream = new FileInputStream(file)) { 
     return fromStream(stream); 
    } 
} 

Вскоре после того, как выяснилось, что это было очень дорого (несколько секунд в МБ) из-за того, что одиночные байты непосредственно считываются из FileInputStream.

Обертывание его в BufferedInputStream решило мою проблему, но это оставило меня с вопросом, почему FileInputStream так безумно дорого.

Канал файла не закрывается или не открывается при чтении байтов, так почему же в первую очередь необходимо использовать буферы?

ответ

3

Если вы читаете байты из небуферизованного потока с использованием метода read(), JVM в конечном итоге сделает многократные системные вызовы для чтения для ОС, чтобы прочитать один байт из файла. (Под капотом JVM, вероятно, вызывает read(addr, offset, count) со счетом 1.)

Стоимость создания салона большого размера. По крайней мере на пару порядков больше, чем обычный вызов метода. Это связано с тем, что имеются значительные накладные расходы:

  • Переключение контекстов между защищенным доменом приложения (непривилегированным) и системным (привилегированным) доменом безопасности. Набор регистров должен быть сохранен, необходимо изменить привязки виртуальной памяти, записи TLB нужно очистить и т. Д.
  • ОС должна выполнять различные дополнительные функции, чтобы гарантировать, что запрашиваемый системный вызов является законным. В этом случае ОС должна выяснить, соответствуют ли запрошенные смещения и счету текущая позиция и размер файла, независимо от того, находится ли адрес в адресном пространстве приложения и отображает ли он его как записываемый. И так далее.

В отличие от этого, если вы используете буферизованный поток, поток попытается прочитать файл из ОС в больших кусках. Это обычно приводит к многотысячному сокращению числа системных вызовов.


Фактически, это НЕ о том, как файлы хранятся на диске. Это правда, что данные в конечном счете должны быть прочитаны блоком за раз и т. Д. Однако ОС достаточно умна, чтобы выполнять собственную буферизацию. Он может даже считывать фрагменты файла, чтобы они были в (ядро) памяти, готовые для приложения, когда он заставляет syscall читать их.

Очень маловероятно, что вызовы с несколькими байтами read() приведут к увеличению объема дискового трафика. Единственный сценарий, когда это правдоподобно, - это долгое время ожидания между каждым read() ... и операционная система повторно использует пространство, в котором он кэшировал блок диска.

1

Когда вы читаете файл, вы должны прочитать его за один раз, потому что это единственное количество, которое поддерживает оборудование. Если вы должны были прочитать символ за раз без буферизации, то, предположив блок 512B, вы бы прочитали тот же блок 512 раз, чтобы прочитать весь блок. Если вы читаете и буферизуете, вы будете обращаться к диску один раз, а затем читать из памяти.

Доступ к диску на несколько порядков медленнее, чем доступ к памяти, o это не отличная идея.

+0

Ну, очень бедная ОС может сделать это именно так, но каждая ОС, имеющая реализацию Java, имеет внутренние кэши системы файловой системы, которые блокируют блок данных с диска, поэтому блок не будет считаться снова. Но даже для чтения из кеша файловой системы требуется системный вызов, который имеет большие накладные расходы по сравнению с чем-либо, что остается внутри пользовательского процесса. См. Ответ Стивена. –