Python 如何有效地让多进程进程读取不可变的大数据
我想使用多处理来使用多个核心来运行一个过程,该过程对大列表中的元素进行成对比较:Python 如何有效地让多进程进程读取不可变的大数据,python,multiprocessing,Python,Multiprocessing,我想使用多处理来使用多个核心来运行一个过程,该过程对大列表中的元素进行成对比较: data = [...] #when loaded this is > 100MB for i in xrange(len(data)-1): parent = data[i] for j in xrange(i,len(data)): child = data[j] #do something with parent and child 因此,如果我设置了一
data = [...] #when loaded this is > 100MB
for i in xrange(len(data)-1):
parent = data[i]
for j in xrange(i,len(data)):
child = data[j]
#do something with parent and child
因此,如果我设置了一个进程队列:
def worker(queue):
while True:
args = queue.get()
if args == 'EOF':
break
f(*args)
def f(data, x, start):
for i in xrange(start,len(data)):
#do stuff
if __name__ == '__main__':
from multiprocessing import Process, Queue, cpu_count
import psycopg2
cur = psycopg2.connect(...).cursor()
data = cur.execute('SELECT * from table')
#when loaded into memory data is > 100MB
other_f_arg = 'some object'
queue = Queue()
#spawn 1 child per core:
workers = [Process(target=worker, args=((queue,)) for cpu in xrange(cpu_count())]
for w in workers:
w.start()
for i in xrange(len(data)-1):
queue.put((data, other_f_arg, i))
queue.put('EOF')
for w in workers:
w.join()
运行此操作时,queue.put会在每次迭代时将数据
推入队列,即使每个进程只需读取一次数据,然后再重新引用。因此,重复的数据传递否定了multiproc的所有优势。如何让每个进程只拾取一次数据
和其他参数的副本
,然后在释放工作人员时只传递动态变量I
更新1:
根据Tim Peters在下面的建议,我决定使用Pool
,但不是使用map
,而是使用带有回调的apply_async
(因为我希望父进程以串行方式对f
的返回进行一些后处理,而不是等待所有提交完成)(因为f
也将返回内存中较大的内容):
有没有办法将子进程中未捕获的异常重定向到控制台?(比如单线程进程中引发的异常?)我这样问是因为
f
中的未捕获异常似乎只是打破了调用apply\u async
的循环,我不会将错误发送到控制台或任何东西。最简单:在Linux-y系统上(一个支持fork()
)的操作系统),在模块级定义数据
。然后所有工作进程都会神奇地看到数据的副本,这是由于神奇的fork()
语义
更具可移植性:使用multiprocessing.Pool()
相反。当您创建池时,您可以指定要运行的初始化函数,以及要传递给该函数的参数。然后,您可以将数据
每个进程只传递一次给某个函数,例如,将其绑定到模块全局名称。然后其他函数可以只引用该模块全局。池()
还支持几种不需要显式管理队列的分发工作(和检索结果)方法。此处不了解足够的详细信息,无法说明这对您的特定问题是好是坏
充实“便携”方式
这里有一种方法:
NUM_CPU = None # defaults to all available CPUs
def worker_init(xdata, xother_f_arg):
global data, other_f_arg
data = xdata
other_f_arg = xother_f_arg
def f(start):
for i in xrange(start, len(data)):
#do stuff
if __name__ == '__main__':
from multiprocessing import Pool
import psycopg2
cur = psycopg2.connect(...).cursor()
data = cur.execute('SELECT * from table')
other_f_arg = 'some object'
pool = Pool(processes=NUM_CPU,
initializer=worker_init,
initargs=(data, other_f_arg))
pool.map(f, xrange(len(data) - 1))
pool.close()
pool.join()
请注意,它的代码也比抛出自己的队列少得多
虽然我不能肯定地运行您的代码,但我希望您最好不要使用多处理
机器传递庞大的数据
,而是让每个工作人员从数据库加载自己的副本。大致如下:
def worker_init(xother_f_arg):
import psycopg2
global data, other_f_arg
other_f_arg = xother_f_arg
cur = psycopg2.connect(...).cursor()
data = cur.execute('SELECT * from table')
编辑-处理错误
并行手段很难在子进程(或线程)中引发异常,因为它们发生在通常与主程序当时正在执行的操作无关的上下文中。处理这一问题的最简单方法是保留对正在创建的AsyncResult
对象的引用,并显式地从它们中获取.get()
结果(失去回拨功能!这只是一个无用的复杂问题)。替换您的:
for i in xrange(len(data)-1):
pool.apply_async(f,
args=(i,),
callback=shim_callback)
例如
# queue up all the work
futures = [pool.apply_async(f, args=(i,))
for i in xrange(len(data) - 1)]
# retrieve results
for fut in futures:
try:
result = fut.get()
except NameExceptionsYouWantToCatchHere as e:
# do whatever you want with the exception
else:
# process result
从文档(当前的Python 2)中:
获取([超时])
当结果到达时返回结果。如果timeout不是None,则
结果未在超时秒内到达,则引发multiprocessing.TimeoutError。如果远程调用引发异常,则get()将重新引发该异常
在Python3中,还有一个map\u async()
方法,以及许多Pool()
方法上的可选error\u回调
参数
注:如果len(数据)
i
它非常大,多处理
机器可以消耗相应的大量RAM来排队所有工作项-apply\u async()
从不阻塞,循环会尽可能快地将工作项排队。在这种情况下,可能需要另一层缓冲。问题是,将“数据”传递给工作人员(=进程)会使数据被复制。因为这是一个相当大的数据集,所以你不会(即使你可以检查确认)有任何速度提升
根据您拥有的数据类型,您应该检查多处理阵列。它可能比“全局”更安全
您可以使用的代码类型是:
from multiprocessing import Process, Queue, cpu_count
import psycopg2
cur = psycopg2.connect(...).cursor()
data = cur.execute('SELECT * from table')
#when loaded into memory data is > 100MB
shared_array = Array('your_data_type', data)
def worker(queue):
while True:
args = queue.get()
if args == 'EOF':
break
f(*args)
def f(data, x, start):
for i in xrange(start,len(data)):
shared array[!!!!]#do stuff
if __name__ == '__main__':
other_f_arg = 'some object'
queue = Queue()
#spawn 1 child per core:
workers = [Process(target=worker, args=((queue,)) for cpu in xrange(cpu_count())]
for w in workers:
w.start()
for i in xrange(len(data)-1):
queue.put((data, other_f_arg, i))
queue.put('EOF')
for w in workers:
w.join()
我的数组是一个dict数组,所以除非数组可以从中自动构建一个cstruct…而且,当工作人员使用它时,数组应该是只读/静态的,所以我把它设为全局数组没有问题,我只需要在主进程生成其内容后让工作人员可以访问它…我正在做一系列的工作sql查询结果的后处理,因此,如果您在使用fork()的系统上运行,我将只执行一次,然后推动数据,而不是让每个工作人员都复制它
,您真的不必到处推送数据。在创建任何辅助进程之前,在主程序中的模块级构建一次数据
。辅助进程是通过fork()
(在具有fork()
的系统上)创建的,因此它们可以在fork()时神奇地获得主程序中所有内容的副本调用了
。我改用了apply_async(请参阅更新的OP),效果很好。每个进程只加载一次数据
。问题:有没有办法将在子进程下运行的f
中未捕获的异常重定向到控制台(就像未捕获的异常会被发送到stderr一样(或windows上的任何等价物)正常情况下)?现在,f
中的运行时异常会导致池终止,并且没有输出。
from multiprocessing import Process, Queue, cpu_count
import psycopg2
cur = psycopg2.connect(...).cursor()
data = cur.execute('SELECT * from table')
#when loaded into memory data is > 100MB
shared_array = Array('your_data_type', data)
def worker(queue):
while True:
args = queue.get()
if args == 'EOF':
break
f(*args)
def f(data, x, start):
for i in xrange(start,len(data)):
shared array[!!!!]#do stuff
if __name__ == '__main__':
other_f_arg = 'some object'
queue = Queue()
#spawn 1 child per core:
workers = [Process(target=worker, args=((queue,)) for cpu in xrange(cpu_count())]
for w in workers:
w.start()
for i in xrange(len(data)-1):
queue.put((data, other_f_arg, i))
queue.put('EOF')
for w in workers:
w.join()