Потоковая передача файлов в Java - PullRequest
8 голосов
/ 18 января 2011

В настоящее время я занимаюсь разработкой приложения для трехмерной графики с использованием JOGL (привязка Java OpenGL).Вкратце, у меня есть огромный двоичный файл ландшафта.Из-за его размера, я должен передавать потоки ландшафта во время выполнения.Поэтому мы явно видим проблему произвольного доступа.Я уже закончил первую (и грязную :)) реализацию (возможно, многопоточную), где я использую глупый подход ... Вот его инициализация:

dataInputStream = new DataInputStream(new BufferedInputStream(fileInputStream,4 * 1024);
dataInputStream.mark(dataInputStream.available());

Икогда мне нужно прочитать (поток) специальный кусок (я уже знаю его «смещение» в файле), я выполняю следующее (позор мне:)):

dataInputStream.reset();
dataInputStream.skipBytes(offset);
dataInputStream.read(whatever I need...);

Поскольку у меня мало опытаэто было первое, о чем я мог подумать :) Итак, до сих пор я прочитал 3 полезные и довольно интересные статьи (я предлагаю вам прочитать их, возможно, если вы заинтересованы в этой теме)

  1. Байт-буферы и память без кучи - Мистер Грегори кажется грамотным в Java NIO.

  2. Совет по Java: как быстро читать файлы [http://nadeausoftware.com/articles/2008/02/java_tip_how_read_files_quickly] - это интересный тест.

  3. Статьи: Настройка производительности ввода-вывода Java [http://java.sun.com/developer/technicalArticles/Programming/PerfTuning/] - Простые рекомендации Sun, но, пожалуйста, прокрутите вниз и посмотрите там раздел «Произвольный доступ»;они показывают простую реализацию RandomAccessFile (RAF) с улучшением само-буферизации.

Mr.Грегори предоставляет несколько * .java файлов в конце своей статьи.Одним из них является сравнительный анализ между FileChannel + ByteBuffer + Mapping (FBM) и RAF.Он говорит, что он заметил 4-кратное ускорение при использовании FBM по сравнению с RAF.Я провел этот тест в следующих условиях:

  1. Смещение (например, место доступа) генерируется случайным образом (в области видимости файла, например, 0 - file.length ());
  2. Размер файла составляет 220 МБ;
  3. 1 000 000 доступов (75% чтения и 25% записи)

Результаты были ошеломляющими:

~ 28 сек дляRAF! ~ 0,2 сек для FBM!

Однако его реализация RAF в этом тесте не имеет собственной буферизации (3-я статья рассказывает об одном), поэтому я думаю, что это "Вызов метода RandomAccessFile.seek ", который так сильно снижает производительность.

Хорошо, теперь после всего, что я узнал, есть 1 вопрос и 1 дилемма:)

Вопрос : Когда мы отображаем файл с помощью «FileChannel.map», копирует ли Java все содержимое файла в MappedByteBuffer?Или это просто подражать?Если он копирует, то использование подхода FBM не подходит для моей ситуации, не так ли?

Дилемма : Зависит от ваших ответов на вопрос ...

  1. Если отображение копирует файл, то кажется, что у меня есть только 2 возможных решения: RAF + самобуферизация (из 3-й статьи) или с использованием позициив FileChannel (не с отображением) ... Какой из них будет лучше?

  2. Если отображение не копирует файл, тогда у меня есть 3 варианта: два предыдущих и Сам FBM .

Редактировать : Вот еще один вопрос.Некоторые из вас говорят, что отображение не копирует файл в MappedByteBuffer.Хорошо, тогда почему я не могу отобразить файл 1 ГБ, я получаю сообщение «не удалось сопоставить» ...

PS Я хотел бы получить полный ответ с советами,так как я не могу найти в интернете непротиворечивую информацию по этой теме.

Спасибо:)

Ответы [ 3 ]

3 голосов
/ 19 января 2011

Нет, данные не буферизируются.MappedByteBuffer ссылается на данные, используя указатель .Другими словами, данные не копируются, они просто отображаются в физическую память.См. API документы , если вы этого еще не сделали.

Файл с отображением в памяти - это сегмент виртуальной памяти, которому была назначена прямая побайтная корреляция снекоторая часть файла или файлового ресурса.Этот ресурс обычно является файлом, который физически присутствует на диске, но также может быть устройством, объектом общей памяти или другим ресурсом, на который операционная система может ссылаться через дескриптор файла.Когда-то эта корреляция между файлом и пространством памяти позволяет приложениям обрабатывать отображенную часть как первичную память.

Источник: Википедия

Если вы собираетесь читать данные довольно часто, рекомендуется хотя бы кэшировать некоторые из них.

2 голосов
/ 18 января 2011

Для файла 220 МБ я бы отобразил все это в виртуальную память.Причина FBM в том, что он не считывает данные в память, а просто делает их доступными.

Примечание.находится в кеше ОС, это будет намного быстрее, независимо от того, как вы это делаете.Вам нужно повторить тест несколько раз, чтобы получить воспроизводимый результат.

1 голос
/ 20 января 2011

Вы заметили, что если вы запустите программу, затем закройте ее, а затем запустите снова, она запускается намного быстрее во второй раз?Это происходит потому, что операционная система кэшировала части файлов, к которым был получен доступ при первом запуске, и не нуждается в доступе к диску для них.Отображение памяти в файле, по существу, позволяет программе получить доступ к этим буферам, таким образом сводя к минимуму копии, сделанные при его чтении.Обратите внимание, что отображение памяти в файле не приводит к тому, что он читается целиком в память;фрагменты, которые вы читаете, читаются с диска по требованию.Если ОС определяет, что памяти мало, она может решить освободить некоторые части сопоставленного файла из памяти и оставить их на диске.

Редактировать: вы хотите FileInputStream.getChannel (). Map (), затем адаптировать его к InputStream, затем подключить к DataInputStream.

...