Java: самый быстрый способ делать случайные чтения на огромных дисковых файлах - PullRequest
6 голосов
/ 27 февраля 2010

У меня умеренно большой набор данных, около 800 МБ или около того, это в основном какая-то большая предварительно вычисленная таблица, которая мне нужна для ускорения вычислений на несколько порядков (создание этого файла заняло несколько компьютеров-многоядерных компьютеров, чтобы произвести используя оптимизированный и многопоточный алгоритм ... мне действительно нужен этот файл).

Теперь, когда он был вычислен один раз, 800 МБ данных доступны только для чтения.

Я не могу удержать его в памяти.

На данный момент это один большой огромный файл размером 800 МБ, но разбиение на более мелкие файлы не проблема, если это может помочь.

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

Какой самый быстрый способ в Java сделать мои случайные чтения в таком файле или файлах? В идеале я должен делать эти чтения из нескольких не связанных потоков (но я мог бы поставить в очередь чтения в один поток, если это необходимо).

Является ли Java NIO подходящим вариантом?

Я не знаком с «файлом сопоставленной памяти»: думаю, я не хочу отображать 800 МБ в памяти.

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

кстати, если люди задаются вопросом, это совсем не то же самое, что вопрос, который я задал недавно:

Java: быстрый дисковый хэш-набор

Ответы [ 4 ]

6 голосов
/ 24 апреля 2010

800 МБ не так много, чтобы загрузить и сохранить в памяти. Если вы можете позволить себе многоядерные машины, копирующие набор данных в течение нескольких дней подряд, вы можете позволить себе дополнительно ГБ или два ОЗУ, не так ли?

Тем не менее, прочитайте о Java java.nio.MappedByteBuffer . Из вашего комментария "Я думаю, что я не хочу отображать 800 МБ в памяти" ясно , что концепция не ясна.

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

Вот шаги, которые я рекомендую вам предпринять:

  1. Создание MappedByteBuffer, который отображает ваш файл данных в MBB. Создание довольно дорогое, так что держите его подальше.
  2. В вашем методе поиска ...
    1. создание массива byte[4]
    2. Звоните .get(byte[] dst, int offset, int length)
    3. байтовый массив теперь будет содержать ваши данные, которые вы можете превратить в значение

И престо! У вас есть ваши данные!

Я большой поклонник MBB и успешно использовал их для таких задач в прошлом.

2 голосов
/ 27 февраля 2010

RandomAccessFile (блокировка) может помочь: http://java.sun.com/javase/6/docs/api/java/io/RandomAccessFile.html

Вы также можете использовать FileChannel.map(), чтобы отобразить область файла в память, а затем прочитать MappedByteBuffer.

Смотри также: http://java.sun.com/docs/books/tutorial/essential/io/rafs.html

1 голос
/ 15 февраля 2012

В случае записи в Java 7 следует рассмотреть AsynchronousFileChannel.

При выполнении случайных записей, ориентированных на запись, для больших файлов (превышение физической памяти, поэтому кэширование не помогает всем) в NTFS, я обнаружил, что AsynchronousFileChannel выполняет в два раза больше операций в однопоточном режиме по сравнению с обычным FileChannel ( для файла объемом 10 ГБ, 160-байтовые записи, полностью случайные записи, некоторый случайный контент, несколько сотен итераций цикла бенчмаркинга для достижения устойчивого состояния, примерно 5300 операций записи в секунду).

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

Я провел микро-бенчмаркинг с RandomAccessFile, чтобы посмотреть, как он будет работать (результаты очень близки к FileChannel, и все же половина производительности AsynchronousFileChannel.

Не уверен, что происходит с многопоточными записями. Это на Java 7, на SSD (SSD на порядок быстрее магнитного и еще на порядок быстрее для небольших файлов, которые помещаются в память).

Будет интересно посмотреть, сохраняются ли те же соотношения в Linux.

1 голос
/ 27 февраля 2010

На самом деле 800 МБ не очень большие. Если у вас 2 ГБ памяти или больше, она может находиться в кеше диска, если не в самом приложении.

...