[英]How to utilize all cores with python multiprocessing
一直在使用Python的multicore
功能花了一个多小时,试图使用Process
和Manager
来并行化一个相当复杂的图遍历功能:
import networkx as nx
import csv
import time
from operator import itemgetter
import os
import multiprocessing as mp
cutoff = 1
exclusionlist = ["cpd:C00024"]
DG = nx.read_gml("KeggComplete.gml", relabel = True)
for exclusion in exclusionlist:
DG.remove_node(exclusion)
#checks if 'memorizedPaths exists, and if not, creates it
fn = os.path.join(os.path.dirname(__file__), 'memorizedPaths' + str(cutoff+1))
if not os.path.exists(fn):
os.makedirs(fn)
manager = mp.Manager()
memorizedPaths = manager.dict()
filepaths = manager.dict()
degreelist = sorted(DG.degree_iter(),key=itemgetter(1),reverse=True)
def _all_simple_paths_graph(item, DG, cutoff, memorizedPaths, filepaths):
source = item[0]
uniqueTreePaths = []
if cutoff < 1:
return
visited = [source]
stack = [iter(DG[source])]
while stack:
children = stack[-1]
child = next(children, None)
if child is None:
stack.pop()
visited.pop()
elif child in memorizedPaths:
for path in memorizedPaths[child]:
newPath = (tuple(visited) + tuple(path))
if (len(newPath) <= cutoff) and (len(set(visited) & set(path)) == 0):
uniqueTreePaths.append(newPath)
continue
elif len(visited) < cutoff:
if child not in visited:
visited.append(child)
stack.append(iter(DG[child]))
if visited not in uniqueTreePaths:
uniqueTreePaths.append(tuple(visited))
else: #len(visited) == cutoff:
if (visited not in uniqueTreePaths) and (child not in visited):
uniqueTreePaths.append(tuple(visited + [child]))
stack.pop()
visited.pop()
#writes the absolute path of the node path file into the hash table
filepaths[source] = str(fn) + "/" + str(source) +"path.txt"
with open (filepaths[source], "wb") as csvfile2:
writer = csv.writer(csvfile2, delimiter=' ', quotechar='|')
for path in uniqueTreePaths:
writer.writerow(path)
memorizedPaths[source] = uniqueTreePaths
############################################################################
start = time.clock()
if __name__ == '__main__':
for item in degreelist:
test = mp.Process(target=_all_simple_paths_graph, args=(DG, cutoff, item, memorizedPaths, filepaths))
test.start()
test.join()
end = time.clock()
print (end-start)
目前 - 虽然运气和魔术 - 它有效(有点)。 我的问题是我只使用了24个内核中的12个。
有人可以解释为什么会出现这种情况吗? 也许我的代码不是最好的多处理解决方案,或者它是我的架构的一个特性[Intel Xeon CPU E5-2640 @ 2.50GHz x18在Ubuntu 13.04 x64上运行]?
编辑:
我设法得到:
p = mp.Pool()
for item in degreelist:
p.apply_async(_all_simple_paths_graph, args=(DG, cutoff, item, memorizedPaths, filepaths))
p.close()
p.join()
然而,工作非常慢 ! 所以我假设我正在使用错误的功能。 希望它有助于澄清我正在努力实现的目标!
EDIT2: .map
尝试:
partialfunc = partial(_all_simple_paths_graph, DG=DG, cutoff=cutoff, memorizedPaths=memorizedPaths, filepaths=filepaths)
p = mp.Pool()
for item in processList:
processVar = p.map(partialfunc, xrange(len(processList)))
p.close()
p.join()
作品,比单曲慢。 时间优化!
太多堆积在这里以便在评论中解决,因此, mp
是multiprocessing
:
mp.cpu_count()
应该返回处理器的数量。 但要测试一下。 有些平台很时髦,而且这些信息并不容易获得。 Python尽其所能。
如果你开始24个进程,他们会完全按照你告诉他们做的;-)看起来像mp.Pool()
对你来说最方便。 您将要创建的进程数传递给其构造函数。 mp.Pool(processes=None)
将使用mp.cpu_count()
作为处理器数。
然后,您可以在Pool
实例上使用.imap_unordered(...)
来跨进程传播您的degreelist
。 或者也许其他一些Pool
方法对你更有效 - 实验。
如果你不能把问题mp.Queue
Pool
的世界观中,你可以创建一个mp.Queue
来创建一个工作队列, .put()
节点(或节点片,以减少开销)工作在主程序中,将worker写入该队列中的.get()
工作项。 询问您是否需要示例。 请注意,您需要在所有“真实”工作项之后将队列值(每个进程一个)放在队列中,以便工作进程可以测试标记以了解它们何时完成。
仅供参考,我喜欢排队因为他们更明确。 许多其他人喜欢Pool
更好,因为他们更神奇;-)
这是一个可执行的原型。 这显示了使用带有Pool
和chunksize
imap_unordered
一种方法,它不需要更改任何函数签名。 当然你必须插入你真正的代码;-)请注意, init_worker
方法允许每个处理器只传递一次“大部分”参数,而不是每个项目中的每个项目degreeslist
。 减少进程间通信量对于速度至关重要。
import multiprocessing as mp
def init_worker(mps, fps, cut):
global memorizedPaths, filepaths, cutoff
global DG
print "process initializing", mp.current_process()
memorizedPaths, filepaths, cutoff = mps, fps, cut
DG = 1##nx.read_gml("KeggComplete.gml", relabel = True)
def work(item):
_all_simple_paths_graph(DG, cutoff, item, memorizedPaths, filepaths)
def _all_simple_paths_graph(DG, cutoff, item, memorizedPaths, filepaths):
pass # print "doing " + str(item)
if __name__ == "__main__":
m = mp.Manager()
memorizedPaths = m.dict()
filepaths = m.dict()
cutoff = 1 ##
# use all available CPUs
p = mp.Pool(initializer=init_worker, initargs=(memorizedPaths,
filepaths,
cutoff))
degreelist = range(100000) ##
for _ in p.imap_unordered(work, degreelist, chunksize=500):
pass
p.close()
p.join()
我强烈建议完全按原样运行,这样你就可以看到它的速度非常快。 然后稍微添加一些东西,看看它会如何影响时间。 例如,只需添加
memorizedPaths[item] = item
到_all_simple_paths_graph()
会大大减慢它的速度。 为什么? 因为每次添加时dict变得越来越大,并且这个过程安全的dict必须在所有进程之间同步(在封面下)。 同步单元是“整个字典” - 没有内部结构,mp机器可以利用它来对共享字典进行增量更新。
如果您负担不起这笔费用,那么就不能使用Manager.dict()
。 聪明的机会比比皆是;-)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.