Чтение десятков тысяч файлов и запись в миллионы файлов на Java

Я делаю некоторые необычные манипуляции с данными. У меня есть 36 000 входных файлов. Одновременно в память может быть загружено больше. Я хочу взять первый байт каждого файла и поместить его в один выходной файл, а затем сделать это снова для второго и так далее. Это не нужно делать в каком-то определенном порядке. Поскольку входные файлы сжаты, их загрузка занимает немного больше времени, и они не могут быть прочитаны по одному байту за раз. Я получаю массив байтов каждого входного файла.

Входные файлы имеют размер около ~ 1-6 МБ без сжатия и ~ 0,3-1 МБ со сжатием (сжатие с потерями). Выходные файлы в конечном итоге представляют собой количество входных файлов в байтах. ~ 36 КБ в моем примере.

Я знаю, что ulimit можно установить в ОС Linux, и то же самое можно сделать в Windows. Несмотря на то, что это число может быть увеличено, я не думаю, что какой-либо ОС понравится, когда миллионы файлов записываются одновременно.

Мое текущее решение состоит в том, чтобы создать 3000 или около того потоков буферизованной записи и загружать каждый входной файл по очереди и записывать 1 байт в 3000 файлов, а затем закрывать файл и загружать следующий ввод. В этой системе каждый входной файл нужно открывать примерно 500 раз.

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

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

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

Однако я не знаю, есть ли лучшее решение, использующее то, о чем я не читал.

EDIT Я благодарен за быстрый ответ. Я знаю, что был расплывчатым в применении того, что я делаю, и я постараюсь исправить это. В основном у меня есть трехмерный массив [images][X][Y]. Я хочу перебирать каждое изображение и сохранять каждый цвет из определенного пикселя на каждом изображении и делать это для всех изображений. Проблема в ограничении памяти.

byte[] пикселей = ((DataBufferByte) ImageIO.read(fileList.get(k)).getRaster().getDataBuffer()).getData();

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

Я не редактирую его как видео, потому что мне нужно было бы получить кадр, затем превратить его в изображение (дорогостоящее преобразование цветового пространства), а затем преобразовать его в byte[] только для того, чтобы получить данные о пикселях в цветовом пространстве RGB.

Я мог бы загрузить каждое изображение и разделить его на ~ 500 частей (размер Y) и записать в отдельные файлы, которые я оставляю открытыми, и записываю для каждого изображения. Выходы были бы легко под гиг. Полученный файл можно было полностью загрузить в память и превратить в массив для последовательной записи в файл.

Промежуточные шаги означают, что я могу разделить нагрузку на сеть, но я пытаюсь сделать это на ноутбуке низкого качества с 4 ГБ оперативной памяти, без графического процессора и i7 низкого качества.

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


person Audo Voice    schedule 27.04.2016    source источник
comment
не знаю что за 3 часть. вам нужно распаковать файл и добавить первые несколько байтов в файл? зачем до 3000 файлов? если у вас более 8 серверов, вы можете использовать hadoop   -  person tgkprog    schedule 28.04.2016
comment
Все входные данные имеют одинаковый размер для данного прогона, но могут иметь очень большой размер между прогонами, а также очень большое количество файлов. Если бы это был 1 МБ и 36000 файлов, то это был бы файл размером 36 ГБ, и это нижний предел. Затем я мог прочитать этот файл очень предсказуемым образом. Каждый байт, который мне нужен, будет точно равен 1 МБ (размер одного входного файла), но, учитывая время, необходимое для его сборки в один массивный файл, действительно ли это намного быстрее? Он будет загружать, а затем выгружать каждый байт из 36 гигабайт в память только для завершения 1 файла. Он сделал бы это 1 миллион раз.   -  person Audo Voice    schedule 28.04.2016


Ответы (2)


Трехфазная операция:

Первый этап: чтение всех входных файлов по одному и запись в один выходной файл. Выходной файл будет ориентирован на записи - скажем, 8-байтовые записи, 4 байта "смещения символа" и 4 байта "кодовой точки символа". Когда вы читаете файл, смещение символов начинается, конечно, с 0, поэтому, если входной файл «ABCD», вы пишете (0, A) (1, B) (2, C) (3, D) . Каждый входной файл открывается один раз, последовательно читается и закрывается. Выходной файл открывается один раз, записывается последовательно, а затем закрывается.

Второй этап: используйте внешнюю сортировку для сортировки 8-байтовых записей промежуточного файла по полю смещения 4-байтовых символов.

Фаза третья: откройте отсортированный промежуточный файл и сделайте один проход по нему. Открывайте новый выходной файл каждый раз, когда изменяется поле индекса символов, и записывайте в этот выходной файл все символы, принадлежащие этому индексу. Входной файл открывается один раз и читается последовательно. Каждый выходной файл открывается, записывается последовательно, а затем закрывается.

Вуаля! Вам нужно место для промежуточного файла и хорошая внешняя сортировка (и место для его рабочих файлов).

Как предполагает @Jorge, как фазу 1, так и фазу 2 можно распараллелить, и на самом деле такая работа, как указано (этапы с 1 по 3), находится именно в сладком месте mapreduce/hadoop.

person davidbak    schedule 27.04.2016

Вы очень расплывчаты, но, возможно, вам поможет просмотр mapreduce. Кажется, такая работа, которую можно было бы распределить.

С предоставленной вами дополнительной информацией я действительно не понимаю, как выполнить эту задачу на обычном оборудовании, таком как 4 ГБ i7, о котором вы упомянули. Ваша проблема выглядит как алгоритм наложения изображений для получения приличного изображения из множества не очень хороших изображений, типичная проблема в астрономической обработке изображений, и я уверен, что она применяется и в других областях. Хороший поиск в обработке астрономических изображений может быть хорошим использованием вашего времени, есть программное обеспечение под названием registax (не уверен, что оно все еще существует), которое делает что-то подобное, но с видеофайлами.

Возвращаясь к математике с салфетками, если вы тратите 1 секунду на открытие файла, вы получаете 10 часов только на открытие файла.

Подход состоял бы в том, чтобы получить какой-нибудь БЫСТРЫЙ диск (SSD), я бы распаковал все файлы в какой-то необработанный формат и сохранил их на диске, оттуда вам придется использовать указатели файлов для чтения непосредственно из файлов, не получая их в память и записать результат в файл прямо на диск.

person Jorge Canelhas    schedule 27.04.2016
comment
Спасибо за указатель на RegiStax (который все еще существует) - я совершенно не знал об этой категории изображений. программное обеспечение для обработки. - person davidbak; 28.04.2016