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