我遇到了使用多处理模块时出现pickle错误的常见问题。
我的确切问题是我需要在我在pool.map
函数中调用它之前给函数调用某个状态,但是这样做会导致attribute lookup __builtin__.function failed
错误找到{{} 3}}。
基于链接的SO答案,看起来在pool.map
中使用函数的唯一方法是调用已定义的函数本身,以便在当前函数的范围之外查找它。
我觉得我上面解释不好,所以这是代码中的问题。 :)
没有游泳池的测试
# Function to be called by the multiprocessing pool
def my_func(x):
massive_list, medium_list, index1, index2 = x
result = [massive_list[index1 + x][index2:] for x in xrange(10)]
return result in medium_list
if __name__ == '__main__':
data = [comprehension which loads a ton of state]
source = [comprehension which also loads a medium amount of state]
for num in range(100):
to_crunch = ((massive_list, small_list, num, x) for x in range(1000))
result = map(my_func, to_crunch)
这可以正常工作,就像预期一样。唯一“错误”的是它很慢。
# (Note: my_func() remains the same)
if __name__ == '__main__':
data = [comprehension which loads a ton of state]
source = [comprehension which also loads a medium amount of state]
pool = multiprocessing.Pool(2)
for num in range(100):
to_crunch = ((massive_list, small_list, num, x) for x in range(1000))
result = pool.map(my_func, to_crunch)
这在技术上有效,但它的速度要慢18倍!减速必须来自于不仅在每次调用时复制两个海量数据结构,而且还在它们传递时对它们进行酸洗/去除。非池版本只需要将引用传递给大量列表,而不是实际列表。
因此,在追踪瓶颈后,我尝试将两个大量列表存储为my_func
内的状态。这样,如果我理解正确,它只需要为每个工人复制一次(在我的情况下,4)。
我将my_func
包装在一个闭包中,将两个列表作为存储状态传递。
def build_myfunc(m,s):
def my_func(x):
massive_list = m # close the state in there
small_list = s
index1, index2 = x
result = [massive_list[index1 + x][index2:] for x in xrange(10)]
return result in medium_list
return my_func
if __name__ == '__main__':
data = [comprehension which loads a ton of state]
source = [comprehension which also loads a medium amount of state]
modified_func = build_myfunc(data, source)
pool = multiprocessing.Pool(2)
for num in range(100):
to_crunch = ((massive_list, small_list, num, x) for x in range(1000))
result = pool.map(modified_func, to_crunch)
但是,这会将pickle错误返回为(基于上面链接的SO问题),您无法从同一范围内调用具有多处理功能的函数。
PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed
那么,有没有解决这个问题的方法呢?
答案 0 :(得分:0)
地图是分配工作量的一种方式。如果你将数据存储在func中,我认为你已经消失了最初的目的。
让我们试着找为什么它慢一点。这不正常,必须有别的东西。
首先,进程数必须适合运行它们的机器。在您的示例中,您使用的是2个进程池,因此总共涉及3个进程。您正在使用的系统上有多少个核心?还在运行什么?在处理数据时系统负载是多少? 该功能对数据有何作用?它访问磁盘吗?或者它可能使用DB,这意味着可能有另一个进程访问磁盘和核心。 记忆呢?存储初始列表是否足够?
正确的实施是您的尝试1。
尝试使用iostat
来分析执行情况。通过这种方式,您可以发现瓶颈。
如果它在cpu上停止,那么你可以尝试对代码进行一些调整。
来自Stackoverflow上的another answer(由我这样没问题复制并将其粘贴到此处:P):
您正在使用收集结果的.map()
然后返回。因此,对于大型数据集,您可能会陷入收集阶段。
如果结果的顺序不重要,您可以尝试使用.imap()
{{1>} 上的迭代器版本,甚至 .map()
< / strong>(从你的例子看来)。
Here是相关文件。值得注意的是:
对于使用较大的chunksize值的非常长的迭代,可以使作业完成比使用默认值1更快。