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