Чтение огромного количества маленьких файлов в последовательности - PullRequest
3 голосов
/ 13 октября 2009

У меня есть такая проблема: у меня есть коллекция маленьких файлов, каждый размером около 2000 байт (все они одинакового размера), и их около 100 000, что составляет около 200 мегабайт пространства. Мне нужно иметь возможность в реальном времени выбирать диапазон в этих файлах. Произнесите файлы от 1000 до 1100 (всего 100 файлов), прочитайте их и отправьте их по сети прилично быстро.

Хорошо, что файлы всегда будут читаться последовательно, то есть всегда будет диапазон, скажем, «из этого файла и еще сотни», а не «этот файл здесь, и этот файл там и т. Д.». ».

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

Текущая схема, которую я придумала, такова: ни один файл не превышает 2000 байт, поэтому вместо нескольких файлов, выделенных на диске, я собираюсь создать один большой файл, содержащий все другие файлы, даже 2048 байт. интервалы с 2 первыми байтами каждого блока 2048, являющимися фактическим размером байта файла, содержащегося в следующих 2046 байтах (размер файла колеблется между 1800 и 1950 байтами или около того), а затем ищите внутри этого файла вместо открытия нового файла обрабатывать каждый файл, который мне нужно прочитать.

Поэтому, когда мне нужно получить файл в позиции X, я просто сделаю X * 2048, прочитайте первые два байта, а затем прочитайте байты от (X * 2048) +2 до размера, содержащегося в первых двух байтах. Этот большой 200-мегабайтный файл будет добавляться только для безопасного чтения, даже когда сериализованный поток ввода / процесс (еще не определен) добавляет к нему больше данных.

Это должно быть выполнимо в Windows, вариант C, но я бы предпочел C #.

Ответы [ 9 ]

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

Есть ли у вас что-то против хранения этих файлов в базе данных?

Простая СУБД значительно ускорила бы поиск и сортировку группы файлов 2k

2 голосов
/ 13 октября 2009

Звучит как разумный вариант.

При чтении данных для диапазона у меня возникнет искушение искать начало «блока данных» и считывать весь лот в память (т. Е. 2048-байтовые буферы для всех файлов) в одном идти. Это уменьшит количество операций ввода-вывода до минимума.

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

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

Было ли в этом вопросе что-то большее, чем просто "это нормальное занятие"?

2 голосов
/ 13 октября 2009

Я думаю, что ваша идея, вероятно, лучшее, что вы можете сделать с достойной работой.

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

Или вы можете просто предварительно загрузить все данные в коллекцию в память, если вы не зависите от поддержания низкого уровня использования ОЗУ (это также будет самый быстрый вариант).

Или вы можете использовать базу данных, но накладные расходы здесь будут существенными.

1 голос
/ 13 октября 2009

Вы можете просто объединить все файлы в один большой файл 'dbase' без заголовка или нижнего колонтитула.

В другом файле 'index' вы можете сохранить положение всех маленьких файлов в 'dbase'. Этот индексный файл очень маленький и может быть полностью кэширован в памяти.

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

1 голос
/ 13 октября 2009

Вы уверены, что никогда не захотите удалить файлы, скажем, с 1200 до 1400? Что происходит, когда вы закончите перевод? Данные заархивированы или будут постоянно расти?

Я действительно не понимаю, почему добавление всех данных в один файл повысит производительность. Вместо этого это может вызвать больше проблем для вас в будущем. Итак, зачем вы их объединяете?

Другие вещи, которые следует учитывать: что произойдет, если массивный файл будет поврежден посередине из-за поврежденных секторов на диске? Похоже, вы все потеряете. Хранение их должно увеличить их живучесть.

Вы, конечно, можете работать с большими файлами, не загружая все это в память, но это не совсем просто, и вам, в конечном счете, придется перейти к некоторому низкоуровневому кодированию, чтобы сделать это. Не сдерживайте себя. Кроме того, что, если файл требует немного ручного редактирования? Большинство программ заставит вас загрузить и заблокировать все это.

Кроме того, наличие одного большого файла означало бы, что у вас не может быть нескольких процессов, читающих / записывающих данные. Это ограничивает масштабируемость.

Если вы знаете, что вам нужны файлы от # 1000 до 1100, вы можете использовать встроенный (c #) код, чтобы получить коллекцию файлов, соответствующих этим критериям.

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

Интересно, что эта проблема напоминает мне вопрос в этом старом SO вопросе:

Это слишком важный вопрос для старшего разработчика Java?

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

Вы можете придерживаться своего решения одного большого файла, но использовать отображение памяти для доступа к нему (см. здесь например). Это может быть немного более производительным, так как вы также избегаете подкачки, а управление виртуальной памятью оптимизировано для передачи кусков по 4096 байт. Афаик, прямой поддержки отображения памяти нет, но здесь - это пример того, как обернуть вызовы WIN32 API для C #.

См. Также здесь по вопросам, связанным с SO.

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

Я бы изменил вашу схему одним способом: вместо того, чтобы читать первые два байта, а затем использовать их для определения размера следующего чтения, я бы сразу прочитал 2 КБ, а затем использовал первые два байта, чтобы определить, сколько байт, которые вы передаете.

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

Другая возможность состоит в том, чтобы упаковать данные для файлов вместе и поддерживать отдельный индекс, чтобы сообщить вам начальную позицию каждого из них. В вашей ситуации это имеет то преимущество, что вместо большого количества маленьких (2 КБ) чтений с диска вы можете объединить произвольное число в одно большое чтение. Получение примерно 64-128 КБ для чтения обычно экономит значительное количество времени.

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...