Лучший способ прочитать огромный файл в МБ в Java

Я просматривал сообщение, и в нем говорится либо использовать BufferedReader, либо MappedByteBuffer. Я решил проверить самостоятельно с файлом 291,0 МБ, но все еще не могу решить

    BufferedReader reader = new BufferedReader(new FileReader("/Users/rachana/part-00000"));
    String line = null;
    while((line = reader.readLine())!=null) {
        System.out.println(line);
    }


    ~~~~~~ Heap utilization in MB ~~~~~~
    Start Date  21:10:20
    End Date 21:17:48
    Time used 448 second
           7.50 min
    Used Memory In MB:28
    Free Memory:81
    Total Memory:109
    Max Memory:1820

С MappedByteBuffer

RandomAccessFile aFile = new RandomAccessFile
                ("/Users/rachana/part-00000", "r");
        FileChannel inChannel = aFile.getChannel();
        MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
        buffer.load(); 
        for (int i = 0; i < buffer.limit(); i++)
        {
            System.out.print((char) buffer.get());
        }
        buffer.clear(); // do something with the data and clear/compact it.
        inChannel.close();
        aFile.close();



~~~~~~ Heap utilization in MB ~~~~~~
 Start Date  21:20:40
 End Date 21:33:52
 Time used 792 sec / 13.2 min
Used Memory In MB:4 
Free Memory:104
Total Memory:109
Max Memory:1820

В нем четко указано, что MappedByteBuffer использует меньше памяти, но больше времени, тогда как BufferedReader использует больше памяти, но меньше времени.

Я пытаюсь найти баланс и способ чтения строки с помощью MappedByteBuffer.

Любое предложение будет полезно


person plzdontkillme    schedule 19.07.2014    source источник
comment
Какой пост вы имеете в виду? Вы должны включить ссылку на него.   -  person Raedwald    schedule 19.07.2014
comment
(a) 300 МБ — это не так много; черт возьми, одна вкладка браузера уже использует столько памяти. (b) Профилируйте свой подход и выясните, достаточно ли он быстр, а затем забудьте о проблеме, пока она не станет быть проблемой. В этом случае ваша производительность, скорее всего, будет ограничена тем, насколько быстро вы можете печатать отдельные символы (подсказка: одновременный вывод большого массива байтов должен быть быстрее), а не тем, насколько быстро вы можете прочитать файл (что должно происходить даже на жестком диске). , займет всего несколько секунд).   -  person Joey    schedule 19.07.2014
comment
Вы профилировали базовый FileInputStream с разными размерами буфера?   -  person ug_    schedule 19.07.2014
comment
Похоже, что вы запускали каждый тест только один раз. Это сделало бы их ущербными. Кроме того, какие меры предосторожности вы предприняли против кэширования файлов операционной системой, искажающей результат второго запуска?   -  person Raedwald    schedule 19.07.2014
comment
Таким образом, ни один из подходов не использует так много памяти (28 МБ против 4 МБ, и даже если тест невелик — кого это волнует?). Если это не действительно проблема, просто используйте подход, который лучше всего решает проблему.   -  person user2864740    schedule 19.07.2014
comment
См. этот связанный вопрос о записи файлов: stackoverflow.com/questions/24646231/   -  person Raedwald    schedule 19.07.2014
comment
Ваш второй случай не выполняет правильное декодирование символов.   -  person Raedwald    schedule 19.07.2014
comment
Удалите System.out.println(line);, так как это даст вам лучшие результаты тестов.   -  person sujithvm    schedule 19.07.2014
comment
Используете ли вы дискеты? Две секунды на мегабайт... потрясающе. IIRC они были намного медленнее, чем я использовал их в прошлый раз. Или результаты просто неверны?   -  person maaartinus    schedule 19.07.2014


Ответы (4)


Самая медленная часть того, что вы делаете, — это печать на экране. Я предлагаю вам не делать этого, и вы будете уверены, что файл MemoryMapped намного быстрее (если вы не печатаете один символ за раз на консоль)

Примечание. Эти два файла не являются взаимозаменяемыми, если вы не используете текстовый файл с кодировкой IS-8859-1 или US-ASCII. BufferedReader предназначен для текста, а файл с отображением памяти — для двоичного кода.

Кстати, нет смысла смотреть на используемую память, если вы игнорируете количество выполненных GC. Если вас интересует только память, используемая в начале и в конце, вам следует выполнить полный GC с помощью System.gc() перед измерением, и я ожидаю, что вы увидите небольшую случайную разницу (может быть отрицательной) в обоих случаях.

Если вы имеете дело с распределениями, вам нужен больший размер eden, например. 2 ГБ, которые начинаются пустыми (после полной сборки мусора), или вы можете использовать профилировщик для измерения распределения. В первом случае строки будут выделять больше всего данных, а во втором больше всего будет создавать запись в консоль.

person Peter Lawrey    schedule 19.07.2014

В нем четко указано, что MappedByteBuffer использует меньше памяти, но больше времени, тогда как BufferedReader использует больше памяти, но меньше времени.

Очевидно, что это не может быть правдой, и это не так. Вы отображаете весь файл размером 300 МБ в память с помощью MappedByteBuffer,, а не с BufferedReader. Объяснение в том, что MappedByteBuffer память не берется из кучи. Он правильно использует память, равно как и размер файла, который намного больше, чем ваш BufferedReader код. Вы просто не измеряете это здесь.

Точно так же ваши измерения времени также недействительны, поскольку в них доминирует System.out.println(), который не является входным и который, как можно надеяться, также не является частью конечного приложения.

Так что ваш бенчмарк совершенно недействителен во всех отношениях.

Используйте BufferedReader. С ним вы можете читать миллионы строк в секунду. Это достаточно быстро.

person user207421    schedule 19.07.2014
comment
Спасибо EJP, ЕСЛИ вас попросят измерить, как вы найдете эталон. - person plzdontkillme; 19.07.2014
comment
Для начала я буду использовать инструменты, которые дают мне реальные результаты. Я не знаю, какой инструмент вы использовали для этого, но либо он неправильный, либо вы используете его неправильно, либо не полностью. Я также проверю свои результаты на вменяемость, прежде чем публиковать их где-либо. - person user207421; 20.07.2014

Я бы выбрал первый, если вы действительно не пытаетесь очистить ствол для оптимизации памяти.

Причины:

  • Читать код проще.
  • Пользователи скорее заметят увеличение скорости на 100%, чем 24 МБ дополнительной памяти.
person George    schedule 19.07.2014

Когда вы выполняете файловый ввод-вывод, вы должны помнить, что операции ввода-вывода, вероятно, будут намного медленнее, чем любая работа, выполняемая ЦП в вашем коде.

Но есть и другие соображения. Оптимизация, как правило, делает код более сложным и трудным для понимания. Чтобы понять ваш MappedByteBuffer код, читатель должен понимать, как MappedByteBuffer работает, в дополнение ко всему, что ему нужно понимать для ввода в файл.

Чтение файлов обычно выполняется. Поэтому вас не должно удивлять, что Java уже предоставляет код, который вам поможет. Этот код будет написан экспертами, протестирован и отлажен. Если у вас нет особых требований, вы всегда должны использовать такой код, а не писать свой собственный. То есть я рекомендую использовать BufferedReader (ваш первый подход).

person Raedwald    schedule 19.07.2014