Java NIO FileChannel и производительность и полезность FileOutputstream

Я пытаюсь выяснить, есть ли разница в производительности (или преимуществах), когда мы используем nio FileChannel против обычного FileInputStream/FileOuputStream для чтения и записи файлов в файловую систему. Я заметил, что на моей машине оба работают на одном уровне, также много раз путь FileChannel медленнее. Могу ли я, пожалуйста, узнать больше, сравнивая эти два метода. Вот код, который я использовал, файл, который я тестирую, составляет около 350MB . Является ли хорошим вариантом использовать classы на основе NIO для ввода / вывода файлов, если я не ищу случайного доступа или других таких расширенных функций?

 package trialjavaprograms; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class JavaNIOTest { public static void main(String[] args) throws Exception { useNormalIO(); useFileChannel(); } private static void useNormalIO() throws Exception { File file = new File("/home/developer/test.iso"); File oFile = new File("/home/developer/test2"); long time1 = System.currentTimeMillis(); InputStream is = new FileInputStream(file); FileOutputStream fos = new FileOutputStream(oFile); byte[] buf = new byte[64 * 1024]; int len = 0; while((len = is.read(buf)) != -1) { fos.write(buf, 0, len); } fos.flush(); fos.close(); is.close(); long time2 = System.currentTimeMillis(); System.out.println("Time taken: "+(time2-time1)+" ms"); } private static void useFileChannel() throws Exception { File file = new File("/home/developer/test.iso"); File oFile = new File("/home/developer/test2"); long time1 = System.currentTimeMillis(); FileInputStream is = new FileInputStream(file); FileOutputStream fos = new FileOutputStream(oFile); FileChannel f = is.getChannel(); FileChannel f2 = fos.getChannel(); ByteBuffer buf = ByteBuffer.allocateDirect(64 * 1024); long len = 0; while((len = f.read(buf)) != -1) { buf.flip(); f2.write(buf); buf.clear(); } f2.close(); f.close(); long time2 = System.currentTimeMillis(); System.out.println("Time taken: "+(time2-time1)+" ms"); } } 

Мой опыт работы с большими размерами файлов был в том, что java.nio быстрее, чем java.io Слишком быстро. Как в диапазоне> 250%. Тем не менее, я устраняю очевидные узкие места, которые, как я полагаю, могут пострадать от вашего микро-теста. Потенциальные области для расследования:

Размер буфера. Алгоритм, который вы в основном используете, это

  • копирование с диска на буфер
  • копирование из буфера на диск

Мой собственный опыт был в том, что этот размер буфера созрел для настройки. Я установил 4 КБ для одной части моего приложения, 256 КБ для другого. Я подозреваю, что ваш код страдает от такого большого буфера. Запустите некоторые тесты с буферами 1KB, 2KB, 4KB, 8KB, 16KB, 32KB и 64KB, чтобы доказать это себе.

Не выполняйте тесты Java, которые читают и записывают на один и тот же диск.

Если да, то вы действительно сравниваете диск, а не Java. Я бы также предположил, что если ваш процессор не занят, то, вероятно, вы столкнулись с каким-то другим узким местом.

Не используйте буфер, если вам это не нужно.

Зачем копировать в память, если ваша цель – другой диск или сетевой адаптер? При больших файлах задержка не является тривиальной.

Как говорили другие, используйте FileChannel.transferTo() или FileChannel.transferFrom() . Ключевым преимуществом здесь является то, что JVM использует доступ ОС к DMA ( прямой доступ к памяти ), если таковой имеется. (Это зависит от реализации, но современные версии Sun и IBM для процессоров общего назначения хороши.) Что происходит, так это то, что данные поступают прямо на / из диска, на шину, а затем в пункт назначения … минуя любую схему через RAM или CPU.

Веб-приложение, в котором я провел дни и ночи, очень тяжело. Я сделал микро-тесты и реальные тесты. И результаты в моем блоге, посмотрите:

  • Показатели производительности реального мира: java.io vs. java.nio
  • Показатели производительности реального мира: java.io vs. java.nio (The Sequel)

Использовать производственные данные и среды

Микро-бенчмарки склонны к искажениям. Если вы можете, приложите усилия для сбора данных именно от того, что вы планируете делать с ожидаемой нагрузкой на ожидаемом вами оборудовании.

Мои контрольные показатели являются прочными и надежными, потому что они происходили на производственной системе, в мясной системе, системе под нагрузкой, собранной в журналах. Не мой SATA-накопитель на 7200 об / мин, а я очень сильно наблюдал за работой JVM на жестком диске.

Что вы работаете? Это важно.

Если вещь, которую вы хотите сравнить, – это производительность копирования файлов, то для теста канала вы должны это сделать:

 final FileInputStream inputStream = new FileInputStream(src); final FileOutputStream outputStream = new FileOutputStream(dest); final FileChannel inChannel = inputStream.getChannel(); final FileChannel outChannel = outputStream.getChannel(); inChannel.transferTo(0, inChannel.size(), outChannel); inChannel.close(); outChannel.close(); inputStream.close(); outputStream.close(); 

Это будет не медленнее, чем буферизация себя от одного канала к другому, и потенциально будет значительно быстрее. Согласно Javadocs:

Многие операционные системы могут передавать байты непосредственно из кеша файловой системы в целевой канал, не копируя их.

Основываясь на моих тестах (Win7 64bit, 6GB RAM, Java6), NIO transferFrom быстро работает только с небольшими файлами и становится очень медленным в больших файлах. Флажок NIO databuffer всегда превосходит стандартный IO.

  • Копирование 1000x2MB

    1. NIO (transferFrom) ~ 2300ms
    2. NIO (прямой datababuffer 5000b flip) ~ 3500 мс
    3. Стандартный IO (буфер 5000b) ~ 6000 мс
  • Копирование 100x20mb

    1. NIO (прямой datababuffer 5000b flip) ~ 4000 мс
    2. NIO (transferFrom) ~ 5000 мс
    3. Стандартный IO (буфер 5000b) ~ 6500 мс
  • Копирование 1x1000mb

    1. NIO (прямой datababuffer 5000b flip) ~ 4500 с
    2. Стандартный IO (буфер 5000b) ~ 7000 мс
    3. NIO (transferFrom) ~ 8000ms

Метод transferTo () работает на кусках файла; не был предназначен как способ копирования на высоком уровне: как скопировать большой файл в Windows XP?

Я тестировал производительность FileInputStream и FileChannel для декодирования файлов с кодировкой base64. В моих экспериментах я тестировал довольно большой файл, а традиционный io всегда был быстрее, чем nio.

FileChannel, возможно, имел преимущество в предыдущих версиях jvm из-за накладных расходов на синхронизацию в нескольких связанных с ним classах, но современные jvm довольно хорошо удаляют ненужные блокировки.

Отвечая на вопрос «полезности» вопроса:

Один довольно тонкий FileChannel использования FileChannel над FileOutputStream заключается в том, что выполнение любой из своих операций блокировки (например, read() или write() ) из streamа, находящегося в прерванном состоянии , приведет к резкому закрытию канала с помощью java.nio.channels.ClosedByInterruptException .

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

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

К сожалению, это так тонко, потому что не учет этого может привести к ошибкам, которые влияют на целостность записи. [1] [2]

Если вы не используете функцию transferTo или неблокирующие функции, вы не заметите разницы между традиционными IO и NIO (2), поскольку традиционные IO-карты для NIO.

Но если вы можете использовать функции NIO, такие как transferFrom / To или хотите использовать Buffers, то, конечно, NIO – это путь.

Мой опыт в том, что NIO намного быстрее с небольшими файлами. Но когда дело доходит до больших файлов, FileInputStream / FileOutputStream работает намного быстрее.

  • Запись / чтение файлов в / из внутренней памяти телефона Android
  • c ++ Чтение из CSV-файла
  • Откройте существующий файл, добавьте одну строку
  • Чтение текстового файла с сервера на Android
  • Java: Найти .txt файлы в указанной папке
  • Google Chrome - отключен доступ к файлам для файлов Chrome для бета-версии 8
  • Android - получить реальный путь .txt-файла, выбранного из файла explorer
  • Написание текстового файла на SD-карту не удается
  • Как добавить значение тайм-аута при использовании Runtime.exec () Java?
  • Замена строк в пакетном файле
  • Получить тип файла в Cocoa
  • Давайте будем гением компьютера.