2016-05-25 5 views
2

КонтекстПочему перехват FileInputStream для переноса FileOutputStream с порядком FilterOutputStream медленнее?

Я пытаюсь получить обратную связь во время передачи данных. Существуют разные случаи, но в конкретном случае я имею дело с копией FileInputStream в FileOutputStream.

Фактический цикл копирования потока выполняется с помощью org.apache.commons.io.IOUtils.

Обратите внимание, что я опытный разработчик, однако я - неофит. Оптимизации JVM мне не известны.

Проблема

Я обертывание FileOutputStream в java.io.FilterOutputStream перехватить передачу и рассчитывать следующим образом:

FileInputStream input = new FileInputStream(new File("path")); 
FileOutputStream output = new FileOutputStream(new File("path2")); 
FilterOutputStream filterOutput = new FilterOutputStream(output); 
IOUtils.copyLarge(input, filterOutput, new byte[32 * 1024]); 

Теперь, когда я делаю это, с фактическим " что-то "удалено (в приведенном выше примере удалена моя реализация с помощью основного фильтра FilterOutputStream, чтобы не повлиять на тесты), скопировав файл 450 Мб, упадет с 5-10 секунд (без упаковки FilterOutputStream) примерно до 8 минут.

Пару фактов

  • Измеренные на Windows x64 8 сердечника машины
  • Копирование из локальной сети на SSD моей машины
  • Один сердечник 100% занят, пока операция не закончится
  • Сеть и диск едва заняты (1-2%)
  • Я тестировал с использованием буферизованных потоков ввода/вывода вокруг моих файловых потоков с различными размерами буфера, а не usi их.
  • Я изменил размер буфера данных.
  • Ни одна из перечисленных выше двух модификаций не оказала существенного влияния на порядок разности величин между и упаковкой FilterOutputStream.

Вопрос

Почему это происходит? И есть ли способ обойти это?

Я предполагаю, что JVM способен обнаруживать стандартные шаблоны копирования файлов и делегировать их непосредственно ОС. Мне кажется немного странным, что он будет делать это в буфферизованных потоках, но не смог бы сделать это с использованием метода write, который косвенен FilterOutputStream.

В настоящее время единственная работа, которую я вижу, заключается в том, чтобы реализовать прослушиватель прямо в цикле копирования вместо того, чтобы конвейеры OutputStreams, но поскольку для этого требуется повторное выполнение цикла вместо использования Apache utils, а также добавление и передача этого прослушивателя на несколько слоев API, я ищу информацию, прежде чем идти по этому пути.

ответ

2

FilterOutputStream будет копировать побайтно по следующему методу:

общественного недействительными записи (байт [] Ь, Int Off, INT LEN) бросает IOException

Запись Len байтов из заданный массив байтов, начинающийся со смещения в этот выходной поток. Метод write FilterOutputStream вызывает метод записи одного аргумента для каждого байта для вывода.

Обратите внимание, что этот метод не вызывает метод записи его базового входного потока с теми же аргументами. Подклассы FilterOutputStream должны обеспечить более эффективную реализацию этого метода.

+0

Я собирался ответить «да, я прочитал комментарий», но потом я прочитал его еще раз ... спасибо, что указал на очевидное. 1-байтная запись, очевидно, не очень удобна для jvm-оптимизации. Фактическая внутренняя реализация FilterOutputStream я отлаживал переопределения, которые записывают (byte [] b, int off, int len) метод для перехвата, но затем перенаправляет его в super вместо out. D'о! – CWilliams

0

A BufferedInputStream - это номер FilterInputStream, с учетом выходной стороны. Можете ли вы использовать их вместо фильтрованных потоков и снова посмотреть на производительность? Это может компенсировать задержку, связанную с IO.