简体   繁体   English

通过python的多处理模块在Pool worker中使用本地内存

[英]Using local memory in Pool workers with python's multiprocessing module

I'm working on implementing a randomized algorithm in python. 我正在努力在python中实现随机算法。 Since this involves doing the same thing many (say N) times, it rather naturally parallelizes and I would like to make use of that. 由于这涉及多次(例如说N次)执行相同的操作,因此自然而然地并行化了,我想利用它。 More specifically, I want to distribute the N iterations on all of the cores of my CPU. 更具体地说,我想在CPU的所有内核上分配N个迭代。 The problem in question involves computing the maximum of something and is thus something where every worker could compute their own maximum and then only report that one back to the parent process, which then only needs to figure out the global maximum out of those few local maxima. 有问题的问题涉及计算某个最大值,因此每个工人都可以计算自己的最大值,然后仅将其报告给父进程,然后该父进程只需从少数几个局部最大值中找出全局最大值即可。 。

Somewhat surprisingly, this does not seem to be an intended use-case of the multiprocessing module, but I'm not entirely sure how else to go about it. 出乎意料的是,这似乎并不是多处理模块的预期用例,但是我不完全确定该怎么做。 After some research I came up with the following solution (toy problem to find the maximum in a list that is structurally the same as my actual one): 经过一番研究,我提出了以下解决方案(在表中查找与实际结构相同的最大值的玩具问题):

import random
import multiprocessing

l = []
N = 100
numCores = multiprocessing.cpu_count()

# globals for every worker
mySendPipe = None
myRecPipe = None

def doWork():
    pipes = zip(*[multiprocessing.Pipe() for i in range(numCores)])
    pool = multiprocessing.Pool(numCores, initializeWorker, (pipes,))
    pool.map(findMax, range(N))

    results = []
    # collate results
    for p in pipes[0]:
        if p.poll():
            results.append(p.recv())
    print(results)

    return max(results)

def initializeWorker(pipes):
    global mySendPipe, myRecPipe
    # ID of a worker process; they are consistently named PoolWorker-i
    myID = int(multiprocessing.current_process().name.split("-")[1])-1
    # Modulo: When starting a second pool for the second iteration of doWork() they are named with IDs 5-8.
    mySendPipe = pipes[1][myID%numCores]
    myRecPipe = pipes[0][myID%numCores]

def findMax(count):
    myMax = 0
    if myRecPipe.poll():
        myMax = myRecPipe.recv()
    value = random.choice(l)
    if myMax < value:
        myMax = value
    mySendPipe.send(myMax)

l = range(1, 1001)
random.shuffle(l)
max1 = doWork()
l = range(1001, 2001)
random.shuffle(l)
max2 = doWork()
return (max1, max2)

This works, sort of, but I've got a problem with it. 这行得通,但是我遇到了问题。 Namely, using pipes to store the intermediate results feels rather silly (and is probably slow). 即,使用管道存储中间结果感觉很愚蠢(可能很慢)。 But it also has the real problem, that I can't send arbitrarily large things through the pipe, and my application unfortunately sometimes exceeds this size (and deadlocks). 但这也有一个真正的问题,就是我无法通过管道发送任意大的东西,不幸的是我的应用有时超过了这个大小(和死锁)。

So, what I'd really like is a function analogue to the initializer that I can call once for every worker on the pool to return their local results to the parent process. 因此,我真正想要的是一个类似于初始化程序的函数,我可以为池中的每个工作程序调用一次,以将其本地结果返回给父进程。 I could not find such functionality, but maybe someone here has an idea? 我找不到这样的功能,但也许有人在这里有想法?

A few final notes: 最后几点注意事项:

  • I use a global variable for the input because in my application the input is very large and I don't want to copy it to every process. 我对输入使用全局变量,因为在我的应用程序中输入非常大,并且我不想将其复制到每个进程。 Since the processes never write to it, I believe it should never be copied (or am I wrong there?). 由于进程从不向其写入数据,因此我认为不应将其复制(否则我错了吗?)。 I'm open to suggestions to do this differently, but mind that I need to run this on changing inputs (sequentially though, just like in the example above). 我乐于接受以其他方式执行此操作的建议,但请注意,我需要在更改输入时运行此操作(不过,就像上面的示例一样)。
  • I'd like to avoid using the Manager-class, since (by my understanding) it introduces synchronisation and locks, which in this problem should be completely unnecessary. 我想避免使用Manager类,因为(据我所知)它引入了同步和锁定,因此在这个问题上应该完全没有必要。

The only other similar question I could find is Python's multiprocessing and memory , but they wish to actually process the individual results of the workers, whereas I do not want the workers to return N things, but to instead only run a total of N times and return only their local best results. 我可以找到的唯一另一个类似的问题是Python的多处理和内存 ,但是他们希望实际处理工作人员的各个结果,而我不希望工作人员返回N件事,而只运行总计N次并仅返回其本地最佳结果。

I'm using Python 2.7.15. 我正在使用Python 2.7.15。


tl;dr: Is there a way to use local memory for every worker process in a multiprocessing pool, so that every worker can compute a local optimum and the parent process only needs to worry about figuring out which one of those is best? tl; dr:是否有一种方法可以在多处理池中为每个工作进程使用本地内存,以便每个工作进程都可以计算局部最优值,而父进程只需要担心找出其中哪个最好?

You might be overthinking this a little. 您可能对此有点想过。 By making your worker-functions (in this case findMax ) actually return a value instead of communicating it, you can store the result from calling pool.map() - it is just a parallel variant of map, after all! 通过使您的辅助函数(在本例中为findMax )实际上返回一个值而不是进行传递,您可以通过调用pool.map()来存储结果- pool.map() ,它只是map的并行变体! It will map a function over a list of inputs and return the list of results of that function call. 它将在输入列表上映射一个函数,并返回该函数调用的结果列表。

The simplest example illustrating my point follows your "distributed max" example: 阐明我的观点的最简单的例子是您的“ distributed max”例子:

import multiprocessing

# [0,1,2,3,4,5,6,7,8]
x = range(9)

# split the list into 3 chunks
# [(0, 1, 2), (3, 4, 5), (6, 7, 8)]
input = zip(*[iter(x)]*3)
pool = multiprocessing.Pool(2)
# compute the max of each chunk:
# max((0,1,2)) == 2
# max((3,4,5)) == 5
# ...
res = pool.map(max, input)
print(res)

This returns [2, 5, 8] . 返回[2, 5, 8] Be aware that there is some light magic going on: I use the built-in max() function which expects iterables as input. 请注意,发生了一些不可思议的事情:我使用内置的max()函数,该函数期望可迭代对象作为输入。 Now, if I would only pool.map over a plain list of integers, say, range(9) , that would result in calls to max(0) , max(1) etc. - not very useful, huh? 现在,如果我只在一个简单的整数列表(例如range(9) pool.map ,那将导致调用max(0)max(1)pool.map不太有用,是吧? Instead, I partition my list into chunks, so effectively, when mapping, we now map over a list of tuples , thus feeding a tuple to max on each call. 取而代之的是,我将列表分成多个块,这样在映射时非常有效,我们现在映射到一个元组列表上 ,从而在每次调用时将一个元组提供给max

So perhaps you have to: 因此,也许您必须:

  • return a value from your worker func 从您的工作者函数中返回一个值
  • think about how you structure your input domain so that you feed meaningful chunks to each worker 考虑一下如何构造输入域,以便向每个工作人员提供有意义的块

PS: You wrote a great first question! PS:您提出了一个很好的第一个问题! Thank you, it was a pleasure reading it :) Welcome to StackOverflow! 谢谢,很高兴阅读它:)欢迎使用StackOverflow!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM