Я новичок в модуле Futures, и у меня есть задача, которая может быть полезна распараллеливание; но я, кажется, не могу точно понять, как настроить функцию для потока и функцию для процесса. Я был бы признателен за любую помощь, которую кто-либо может пролить по этому вопросу.
Я запускаю оптимизацию роя частиц (PSO). Не вдаваясь в подробности самого PSO, вот базовая структура моего кода:
Существует класс Particle
с методом getFitness(self)
(который вычисляет некоторую метрику и сохраняет ее в self.fitness
). Симуляция PSO имеет несколько экземпляров частиц (легко более 10, 100 или даже 1000 для некоторых симуляций).
Время от времени мне приходится вычислять пригодность частиц. В настоящее время я делаю это в цикле for:
for p in listOfParticles:
p.getFitness(args)
Однако я заметил, что пригодность каждой частицы можно вычислить независимо друг от друга. Это делает вычисление пригодности главным кандидатом на распараллеливание. Действительно, я мог бы сделать map(lambda p: p.getFitness(args), listOfParticles)
.
Теперь я могу легко сделать это с помощью futures.ProcessPoolExecutor
:
with futures.ProcessPoolExecutor() as e:
e.map(lambda p: p.getFitness(args), listOfParticles)
Поскольку побочные эффекты вызова p.getFitness
хранятся в каждой частице, мне не нужно беспокоиться о возврате от futures.ProcessPoolExecutor()
.
Все идет нормально. Но теперь я замечаю, что ProcessPoolExecutor
создает новые процессы, а значит копирует память, что медленно. Я хотел бы иметь возможность делиться памятью, поэтому я должен использовать потоки. Это хорошо, пока я не понял, что запуск нескольких процессов с несколькими потоками внутри каждого процесса, вероятно, будет быстрее, поскольку несколько потоков по-прежнему работают только на одном процессоре моей милой 8-ядерной машины.
Вот где я столкнулся с проблемой:
Судя по примерам, которые я видел, ThreadPoolExecutor
работает с list
. Как и ProcessPoolExecutor
. Таким образом, я не могу сделать ничего итеративного в ProcessPoolExecutor
, чтобы отдать на откуп ThreadPoolExecutor
, потому что тогда ThreadPoolExecutor
получит один объект для работы (см. мою попытку, опубликованную ниже).
С другой стороны, я не могу нарезать listOfParticles
самостоятельно. , потому что я хочу, чтобы ThreadPoolExecutor
творил свою собственную магию, чтобы выяснить, сколько потоков требуется.
Итак, большой вопрос (наконец-то):
как мне структурировать мой код, чтобы я мог эффективно распараллелить следующие процессы, используя как процессы, так и потоки:
for p in listOfParticles:
p.getFitness()
Это то, что я пытался, но я бы не осмелился запустить его, потому что я знаю, что это не сработает:
>>> def threadize(func, L, mw):
... with futures.ThreadpoolExecutor(max_workers=mw) as executor:
... for i in L:
... executor.submit(func, i)
...
>>> def processize(func, L, mw):
... with futures.ProcessPoolExecutor() as executor:
... executor.map(lambda i: threadize(func, i, mw), L)
...
Я был бы признателен за любые мысли о том, как это исправить или даже о том, как улучшить мой подход.
Если это имеет значение, я на python3.3.2
getFitness()
? Проблема с потоками в CPython заключается в том, что они подходят только для задач, связанных с вводом-выводом, потому что CPython имеет глобальную блокировку интерпретатора (GIL), которая разрешает выполняться только одному потоку за раз. Если, например,getFitness()
запускает код Python, привязанный к процессору, GIL заставит многопоточность работать медленнее, чем без многопоточности (тогда многопоточность просто добавляет дополнительные накладные расходы для переключения контекста). Но если, например,getFitness()
запускает функцию модуля расширения, которая освобождает GIL, то может помочь многопоточность (например, многие функцииnumpy
освобождают GIL). - person Tim Peters   schedule 24.11.2013getFitness
интерпретирует информацию, закодированную в частице, как начальную семантику нейронной сети, запускает полученную нейронную сеть и вычисляет выходную ошибку (эта выходная ошибка является приспособленностью — ну, на самом деле, ее обратной величиной). Таким образом, я считаю, что эта функция будет больше привязана к процессору, чем к вводу-выводу (я сделал все, что связано с нейронной сетью, с нуля, и это все списки классов и их умножения). Так что, возможно, потоки не слишком помогут в этой конкретной ситуации, но я все же хотел бы иметь возможность использовать ThreadPool в ProcessPool для применимых проблем. - person inspectorG4dget   schedule 25.11.2013