Java NIO FileChannel против производительности / полезности FileOutputstream - PullRequest
164 голосов
/ 22 октября 2009

Я пытаюсь выяснить, есть ли разница в производительности (или преимуществах), когда мы используем 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");
    }
}

Ответы [ 7 ]

194 голосов
/ 07 ноября 2009

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

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

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

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

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

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

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

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

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

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

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

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

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

На чем ты бежишь? Это имеет значение.

36 голосов
/ 22 октября 2009

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

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:

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

7 голосов
/ 03 ноября 2011

Основываясь на моих тестах (64-битная Win7, 6 ГБ ОЗУ, Java6), NIO TransferFrom работает быстро только с небольшими файлами и очень медленно на больших файлах. Отворот буфера данных NIO всегда превосходит стандартный ввод-вывод.

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

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

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

    1. NIO (прямой откат данных 5000b) ~ 4500 с
    2. Стандартный ввод-вывод (буфер 5000b) ~ 7000 мс
    3. NIO (передача от) ~ 8000 мс

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

5 голосов
/ 23 февраля 2017

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

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

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

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

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

3 голосов
/ 22 октября 2009

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

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

2 голосов
/ 22 апреля 2013

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

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

0 голосов
/ 22 октября 2009

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

...