Я пытаюсь выяснить, есть ли разница в производительности (или преимуществах), когда мы используем nio по FileChannel
сравнению с обычным FileInputStream/FileOuputStream
для чтения и записи файлов в файловую систему. Я заметил, что на моей машине оба работают на одном уровне, также во много раз FileChannel
медленнее. Могу ли я узнать подробности, сравнивая эти два метода? Вот код, который я использовал, файл, с которым я тестирую, находится рядом 350MB
. Является ли хорошим вариантом использовать основанные на 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
optimization
file
nio
operations
Кешав
источник
источник
transferTo
/transferFrom
будет более обычным для копирования файлов. Какой бы метод не делал ваш жесткий диск быстрее или медленнее, хотя я думаю, что это может быть проблемой, если он читает маленькие куски за раз и заставляет голову тратить слишком много времени на поиск.Ответы:
Мой опыт работы с файлами большего размера был
java.nio
быстрее, чемjava.io
. Существенно быстрее. Как в диапазоне> 250%. Тем не менее, я устраняю очевидные узкие места, от которых, как я полагаю, может пострадать ваш микро-тест. Потенциальные области для расследования:Размер буфера. Алгоритм, который у вас есть,
Мой собственный опыт показывает, что этот размер буфера созрел для настройки. Я остановился на 4 КБ для одной части моего приложения, 256 КБ для другой. Я подозреваю, что ваш код страдает от такого большого буфера. Запустите несколько тестов с буферами 1 КБ, 2 КБ, 4 КБ, 8 КБ, 16 КБ, 32 КБ и 64 КБ, чтобы доказать это самим.
Не выполняйте тесты Java, которые читают и пишут на один и тот же диск.
Если вы это сделаете, то вы действительно тестируете диск, а не Java. Я также хотел бы предложить, что если ваш процессор не занят, то вы, вероятно, испытываете некоторые другие узкие места.
Не используйте буфер, если вам это не нужно.
Зачем копировать в память, если вашей целью является другой диск или сетевой адаптер? При использовании больших файлов задержка становится нетривиальной.
Как и другие сказали, использовать
FileChannel.transferTo()
илиFileChannel.transferFrom()
. Ключевым преимуществом здесь является то, что JVM использует доступ ОС к прямому доступу к памяти (если есть). (Это зависит от реализации, но современные версии Sun и IBM для процессоров общего назначения хороши.) Что происходит, если данные направляются прямо на диск / с диска, на шину, а затем к месту назначения ... в обход любой цепи через RAM или процессор.Веб-приложение, над которым я работал день и ночь, очень тяжелое. Я также сделал микро тесты и реальные тесты. И результаты на моем блоге, посмотрите:
Используйте производственные данные и среды
Микро-тесты подвержены искажениям. Если вы можете, постарайтесь собрать данные именно из того, что вы планируете делать, с ожидаемой нагрузкой на ожидаемое оборудование.
Мои тесты являются надежными и надежными, потому что они проводились на производственной системе, сложной системе, загруженной системе, собранной в журналах. Не 2,5-дюймовый SATA-накопитель 7200 об / мин в моем ноутбуке, пока я интенсивно наблюдал, как JVM работает на моем жестком диске.
На чем ты бежишь? Это имеет значение.
источник
Если вы хотите сравнить производительность копирования файла, то для теста канала вы должны сделать следующее:
Это не будет медленнее, чем буферизация себя с одного канала на другой, и потенциально будет значительно быстрее. Согласно Javadocs:
источник
Основываясь на моих тестах (64-битная Win7, 6 ГБ ОЗУ, Java6), NIO TransferFrom работает быстро только с небольшими файлами и очень медленно работает с большими файлами. Отворот буфера данных NIO всегда превосходит стандартный ввод-вывод.
Копирование 1000x2MB
Копирование 100x20mb
Копирование 1x1000mb
Метод TransferTo () работает с фрагментами файла; не задумывался как метод копирования файлов высокого уровня: Как скопировать большой файл в Windows XP?
источник
Отвечая на «полезность» части вопроса:
Один довольно тонкий прием использования
FileChannel
overFileOutputStream
является то, что выполнение любой из его операций блокировки (например,read()
илиwrite()
) из потока, который находится в прерванном состоянии , приведет к внезапному закрытию канала сjava.nio.channels.ClosedByInterruptException
.Теперь, это может быть хорошо, если все, что
FileChannel
использовалось, является частью основной функции потока, и дизайн принял это во внимание.Но это также может быть неприятно, если использовать какую-то вспомогательную функцию, такую как функция регистрации. Например, вы можете обнаружить, что ваш вывод журнала внезапно закрыт, если функция ведения журнала вызвана потоком, который также прерван.
К сожалению, это настолько тонко, потому что отсутствие учета этого может привести к ошибкам, которые влияют на целостность записи. [1] [2]
источник
Я проверил производительность FileInputStream и FileChannel для декодирования файлов в кодировке base64. В моих экспериментах я тестировал довольно большой файл, и традиционный io всегда был немного быстрее, чем nio.
FileChannel мог иметь преимущество в предыдущих версиях jvm из-за накладных расходов на синхронизацию в нескольких классах, связанных с io, но современные jvm довольно хороши в удалении ненужных блокировок.
источник
Если вы не используете функцию TransferTo или неблокирующие функции, вы не заметите разницы между традиционным IO и NIO (2), потому что традиционный IO отображается на NIO.
Но если вы можете использовать функции NIO, такие как TransferFrom / To, или хотите использовать буферы, то, конечно, NIO - это путь.
источник
Мой опыт показывает, что NIO намного быстрее с небольшими файлами. Но когда дело доходит до больших файлов, FileInputStream / FileOutputStream намного быстрее.
источник
java.nio
с большими файлами быстрее , чем сjava.io
меньшими.java.nio
быстрый, пока файл достаточно мал, чтобы быть сопоставленным с памятью. Если он становится больше (200 МБ и более),java.io
это быстрее.FileChannel.read()
. Не существует единственного подхода к чтению файлов с использованиемjava.nio
.