这是我最近的问题Avoiding race conditions in Python 3's multiprocessing Queues的扩展。希望这个版本的问题更加具体。
TL; DR:在使用multiprocessing.Queue
从队列中提供工作进程的多处理模型中,为什么我的工作进程如此空闲?每个进程都有自己的输入队列,因此它们是为了共享队列的锁定而不是互相争斗,但队列花了很多时间实际上是空的。主进程是运行I / O绑定线程 - 是否会减慢输入队列的CPU绑定填充?
我试图在某个约束下找到N个集合的笛卡尔乘积的最大元素,每个集合具有M_i个元素(对于0 <= i True
时,组合符合约束。在我的问题中,我试图找到其元素权重最大的组合:sum(element.weight for element in combination)
。
我的问题规模很大,但我公司的服务器也是如此。 我正在尝试将以下串行算法重写为并行算法。
from operator import itemgetter
from itertools import product # Cartesian product function from the std lib
def optimize(sets):
"""Return the largest (total-weight, combination) tuple from all
possible combinations of the elements in the several sets, subject
to the constraint that is_feasible(combo) returns True."""
return max(
map(
lambda combination: (
sum(element.weight for element in combination),
combination
),
filter(
is_feasible, # Returns True if combo meets constraint
product(*sets)
)
),
key=itemgetter(0) # Only maximize based on sum of weight
)
我目前的多处理方法是创建工作进程并使用输入队列提供它们的组合。当工人收到poison pill时,他们会将他们在输出队列中看到的最佳组合放置并退出。我从主进程的主线程填充输入队列。这种技术的一个优点是我可以从主进程生成一个辅助线程来运行一个监视工具(我只能使用一个REPL来查看到目前为止已处理了多少组合以及队列有多充分)。
+-----------+
in_q0 | worker0 |----\
/-------+-----------+ \
+-----------+ in_q1 +-----------+ \ out_q +-----------+
| main |-----------| worker1 |-----------| main |
+-----------+ +-----------+ / +-----------+
\-------+-----------+ /
in_q2 | worker2 |----/
+-----------+
我最初让所有工作人员从一个输入队列中读取,但发现他们都没有击中CPU。确定他们花了所有时间等待queue.get()取消阻止,我给了他们自己的队列。这增加了对CPU的压力,所以我认为工人更活跃。但是,队列大部分时间都是空的! (我从我提到的监控REPL中知道这一点)。这告诉我,填充队列的主循环很慢。这是循环:
from itertools import cycle
main():
# (Create workers, each with its own input queue)
# Cycle through each worker's queue and add a combination to that queue
for combo, worker in zip(product(*sets), cycle(workers)):
worker.in_q.put(combo)
# (Collect results and return)
我猜测瓶颈是worker.in_q.put()
。我该如何加快速度?我的第一直觉是让工作者变慢,但这没有意义......监视器线程是否经常停止循环的问题?我怎么能说出来?
或者,是否存在另一种实现此方法的方法,它不需要等待很多锁?
答案 0 :(得分:4)
你的元素是什么样的?可能是将它们腌制成队列很慢,这显然是一个瓶颈。请注意,每个元素都经过一次又一次的独立腌制。
如果是这种情况,这种方法可能有所帮助:
pickle.dumps
一次,然后将相同的字符串发送给每个工作者,或者可能通过共享内存或其他任何内容)。product(my_A_subset, *other_sets)
(可能以不同方式排序)的快乐方式开始,在每个作业(或每三个作业或其他任何作业)之间轮询某种停止信号。这不需要通过队列,一位共享内存值可以正常工作。