Как я могу увеличить скорость чтения Perl для каталога с 250 000 файлов? - PullRequest
4 голосов
/ 10 апреля 2009

Я использую Perl readdir для получения списка файлов, однако каталог содержит более 250 000 файлов, что приводит к длительному (более 4 минут) выполнению readdir и использует более 80 МБ ОЗУ. Поскольку это должно было быть повторяющимся заданием каждые 5 минут, это время задержки не будет приемлемым.

Дополнительная информация: Другая работа будет заполнять каталог (один раз в день) для сканирования. Этот Perl-скрипт отвечает за обработку файлов. Количество файлов указывается для каждой итерации сценария, в настоящее время 1000 для каждого запуска. Скрипт Perl должен запускаться каждые 5 минут и обрабатывать (если применимо) до 1000 файлов. Предел количества файлов, предназначенный для обеспечения возможности обработки в нисходящем направлении, поскольку Perl помещает данные в базу данных, что запускает сложный рабочий процесс.

Есть ли другой способ получения имен файлов из каталога, в идеале ограниченный 1000 (устанавливается переменной), который значительно увеличил бы скорость этого скрипта?

Ответы [ 6 ]

9 голосов
/ 10 апреля 2009

Что именно вы имеете в виду, когда говорите, что readdir занимает минуты и 80 МБ? Можете ли вы показать эту конкретную строку кода? Вы используете readdir в скалярном или списочном контексте?

Вы делаете что-то вроде этого:

foreach my $file ( readdir($dir) ) { 
   #do stuff here
}

Если это так, вы читаете весь список каталогов в память. Не удивительно, что это занимает много времени и много памяти.

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

Исправление для этого - использовать цикл while и использовать readdir в скалярном контексте.

while ( 
    defined( my $file = readdir $dir )
 ) {

    # do stuff.

}

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

7 голосов
/ 10 апреля 2009

Решение может быть в другом конце: в сценарии, который заполняет каталог ...

Почему бы не создать древовидную структуру для хранения всех этих файлов и таким образом иметь множество каталогов с управляемым количеством файлов в каждом?

Вместо того, чтобы создавать «mynicefile.txt», почему бы не «m / my / mynicefile» или что-то в этом роде?

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

2 голосов
/ 10 апреля 2009

Вы говорите, что содержимое попадает туда, распаковав zip-файл (ы). Почему бы вам просто не поработать с zip-файлами вместо создания / использования 250 КБ файлов в одном каталоге?

В основном - чтобы ускорить его, вам не нужны конкретные вещи в perl, а скорее на уровне файловой системы. Если вы на 100% уверены, что вам нужно работать с 250-килобайтными файлами в каталоге (что я не могу себе представить, когда потребуется что-то подобное) - вам гораздо лучше найти лучшую файловую систему, чтобы справиться с этим, чем найти некоторый «магический» модуль в Perl, который сканировал бы его быстрее.

2 голосов
/ 10 апреля 2009

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

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

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

1 голос
/ 10 апреля 2009

Вы не сможете ускорить readdir, но вы можете ускорить задачу мониторинга каталога. Вы можете запросить обновления у ОС - например, в Linux есть inotify. Вот статья об использовании этого:

http://www.ibm.com/developerworks/linux/library/l-ubuntu-inotify/index.html?ca=drs-

Вы можете использовать Inotify от Perl:

http://metacpan.org/pod/Linux::Inotify2

Разница в том, что у вас будет одно долго работающее приложение вместо скрипта, запускаемого cron. В приложении вы будете хранить очередь файлов, которые являются новыми (как предусмотрено inotify). Затем вы устанавливаете таймер на каждые 5 минут и обрабатываете 1000 элементов. После этого управление возвращается в цикл обработки событий, и вы либо просыпаетесь через 5 минут и обрабатываете еще 1000 элементов, либо inotify отправляет вам еще файлы для добавления в очередь.

(Кстати, вам понадобится цикл обработки событий для обработки таймеров; я рекомендую EV.)

1 голос
/ 10 апреля 2009

Наверное, нет. Я полагаю, что большую часть времени вы читаете в каталоге.

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

Вы пробовали просто readdir() через каталог без какой-либо другой обработки вообще, чтобы получить базовый уровень?

...