0

我必须从sklearn KDTree中查询大量的向量,它是搜索器类的路径。我试图使用python multiprocessing并行查询它们,但并行代码与单一版本几乎相同(或更多)的时间。Python多处理:检查内存是共享还是被复制

import time, numpy as np 
from sklearn.neighbors import KDTree 
from multiprocessing import Pool 

def glob_query(arg, **kwarg): 
    return Searcher.query(*arg, **kwarg) 

class Searcher: 
    def __init__(self, N, D): 
      self.kdt = KDTree(np.random.rand(N,D), leaf_size=30, metric="euclidean") 

    def query(self, X): 
      return self.kdt.query(X, k=5, return_distance=False) 

    def query_sin(self, X): 
      return [self.query(x) for x in X] 

    def query_par(self, X): 
      p = Pool(4) 
      return p.map(glob_query, zip([self]*len(X), X)) 

if __name__=="__main__": 
    N = 1000000  # Number of points to be indexed 
    D = 50   # Dimensions 
    searcher = Searcher(N, D) 

    E = 100   # Number of points to be searched 
    points = np.random.rand(E, D) 

    # Works fine 
    start = time.time() 
    searcher.query_sin(points) 
    print("Time taken - %f"%(time.time()-start)) 

    # Slower than single core 
    start = time.time() 
    print searcher.query_par(points) 
    print("Time taken - %f"%(time.time()-start)) 

Time taken - 28.591089 
Time taken - 36.920716 

我想知道

  • 如果我的kd树被在每个工作线程
  • 复制是那里parallelise搜索的另一种方法(使用凄楚?)

回答

1

Pool启动当时基本上是父进程的副本的进程。既然你在游泳池之前创建了kd-tree,所有的孩子都应该拥有它。

请注意,创建新进程需要时间和内存。

每次映射函数返回一个结果时,都会使用IPC将结果发送回父进程。根据返回的数据的大小,这可能会产生很大的开销。

但是在您尝试改进之前,测量值为。如果您不知道是什么原因导致问题,则无法解决问题。

您可以使用分析器来查看程序花​​费时间在哪里。

或者您可以使用imapimap_unordered,它会在结果上返回一个迭代器。打印开始imap之前的时间。在query方法中运行查询之前和之后,打印当前时间。还打印迭代器产生结果的时间。这应该会让你知道程序在哪里花费大部分时间。

1

那么你的代码似乎很好。我想这个额外的时间来自在池中创建4个进程。尝试在Searcher的init方法中创建池以查看它是否确实如此。

关于你的问题,当你打开一个新进程时,传递的对象被复制到新进程中。

如果您在Windows上运行比,每次启动会导致蟒蛇重新导入新过程中的所有代码和咸菜,以将它们复制你的变量的过程(这可能是短期运行的进程昂贵)

在linux中所有这些都被替换os.fork

+0

如果我在'init'创建池,我得到一个错误说'池对象不能处理或pickled' – kampta

+0

@kampta之间进行传递:如果你确实是最终需要传递'pool',你可以做所以使用'pathos' ......实质上,你可以进行嵌套的'map'调用(或'map'变体)。 –

2

我是pathos作者。正如其他答案中所述,multiprocessing将对象复制到新进程。 pathos的情况也是如此,因为它建立在multiprocessing的分叉上。 (1)它可以提供更好的序列化,(2)更灵活的map,可以采用多个参数,(3)在启动多个Pool时删除一些开销...但这听起来不像您的情况。

如果计算真的很单独,那么multiprocessing.dummy中的线程可能是更好的选择。你可以尝试一下,看看它是否能加快速度。界面是一样的,所以你的代码中很少需要编辑。另外,如其他人所暗示的,如果您不需要维护结果的顺序,imap_unordered通常是Poolmap函数中最快的。

通常最好的方法就是尝试几种map,并查看哪种情况最快。

+0

我尝试过'multiprocessing.dummy',它肯定比'multiprocessing'快,但是不会比单核方法快(我猜这是因为一次只能执行1个线程)。我会尝试'imap_unordered'。 – kampta

相关问题