2015-04-13 3 views
0

Мне нужно прочитать каждый файл в дереве каталогов, начиная с заданного корневого местоположения. Я хотел бы сделать это как можно быстрее, используя параллелизм. У меня есть 48 ядер в моем распоряжении и 1 TB ram, поэтому ресурсы потоков не являются проблемой. Мне также нужно регистрировать каждый файл, который был прочитан.Параллельный каталог walk python

Я посмотрел на использование joblib, но не смог объединить joblib с os.walk.

я могу думать о двух способах:

  • ходить по дереву и добавить все файлы в очередь или список и есть уборщица пул потоков DEQUEUE файлов - лучший балансировки нагрузки, может быть, больше времени из-за начальную ходьбу & queue overhead
  • порождает потоки и статически назначает части дерева каждому потоку - низкая балансировка нагрузки, отсутствие начальной ходьбы, назначение каталогов на основе какого-либо хэша.

или есть лучший способ?

EDIT Эксплуатационное хранение не является проблемой. Предположим, существует бесконечно быстрое хранилище, которое может обрабатывать бесконечное число параллельных читает

EDIT удален многоузловой ситуации держать фокус на параллельной директории ходьбы

+0

Какое хранилище вы пытаетесь прочитать? Вы упоминаете масштабирование по узлам, поэтому я предполагаю, что мы не просто говорим о типичном HDD/SSD здесь? – dano

+2

Основываясь на комментарии дано, это больше похоже на ограниченную работу с жестким диском, чем на работу с ограниченным процессором. Если вы делаете много потоков, вы, честно говоря, можете запустить * SLOWER *, чем один процессор, так как вы собираетесь заставить многих искать !! – WakkaDojo

+0

Право - узкое место здесь, скорее всего, диск ввода-вывода. Неважно, сколько процессоров вы бросаете на проблему, если ваш диск не поддерживает параллельные чтения. – dano

ответ

4

Самый простой подход, вероятно, использовать multiprocessing.Pool для обработки результатов выход os.walk, выполненный в основном процессе.

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

import itertools 
import multiprocessing 

def worker(filename): 
    pass # do something here! 

def main(): 
    with multiprocessing.Pool(48) as Pool: # pool of 48 processes 

     walk = os.walk("some/path") 
     fn_gen = itertools.chain.from_iterable((os.path.join(root, file) 
               for file in files) 
               for root, dirs, files in walk) 

     results_of_work = pool.map(worker, fn_gen) # this does the parallel processing 

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

+0

очень круто! необходимы некоторые изменения, хотя для этого необходимо: 1) os.path.walk следует изменить на os.walk 2) использование «с multiprocessing.Pool (48) в качестве пула» приведет к AttributeError: объект «Pool» не имеет атрибут '__exit__'. Я использовал контекстное решение отсюда: http://stackoverflow.com/questions/27065237/attributeerror-pool-object-has-no-attribute-exit – powerrox

+0

Генераторы, возвращенные в fn_gen, не могут быть сериализованы, поэтому код по-прежнему не работает после изменения. – powerrox

+0

Это сработало: results_of_work = pool.map (рабочий, [list (j) для j в fn_gen]) – powerrox

Смежные вопросы