0

我必须从 sklearn KDTree 中查询大量向量,这是搜索器类的路径。我正在尝试使用 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-tree 被复制到每个工作线程
  • 是否有另一种并行搜索的方法(使用 pathos?)
4

3 回答 3

2

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

如果单独计算真的很轻,那么线程可能multiprocessing.dummy是一个更好的方法。您可以尝试一下,看看它是否加快了速度。界面是相同的,因此您的代码几乎不需要编辑。此外,正如其他人所建议的那样,如果您不需要维护结果的顺序,imap_unordered通常mapPool.

通常最好的办法就是尝试几种map,看看哪种最适合你的情况。

于 2015-09-21T02:01:55.130 回答
1

Pool启动进程基本上是当时父进程的副本。由于您在池之前创建了 kd-tree,因此所有孩子都应该拥有它。

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

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

但在您尝试改进之前,请测量. 如果您不知道问题的原因,您就无法解决问题。

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

或者您可以使用imapor imap_unorderedwhich 返回结果的迭代器。打印开始之前的时间imap。在方法中运行查询之前和之后打印当前时间query。还打印迭代器产生结果的时间。这应该让您了解程序大部分时间花在哪里。

于 2015-09-20T09:13:43.273 回答
1

那么你的代码看起来不错。我猜额外的时间来自于在池中创建 4 个进程。尝试在 Searcher 的 init 方法中创建池,看看是否确实如此。

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

如果您在 Windows 中运行它,那么您启动的每个进程都会导致 python 在新进程中重新导入您的所有代码,并腌制您的变量以复制它们(这对于短期运行的进程来说可能很昂贵)

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

于 2015-09-20T09:14:58.153 回答