当函数需要大的数据结构作为参数时,如何有效地使用Pool.starmap? 背景:我想要实现的目标:我尝试的第1部分:我尝试过的第二部分:问题:

问题描述

背景:

我在笔记本中接管了一些代码,这些笔记本大量使用了全局变量,一方面使使用Pool.imap变得容易,但另一方面却使其难以阅读,调试和移出一个Jupyter笔记本进入现实世界:

方法调用一个方法query_rec即可在给定点周围执行KNN搜索。请注意,points_adjustedtimes是在函数外部定义的。 query_rec使用在其范围之外定义的KDTree

def get_neighbors(i):
    point = points_adjusted[i] + (times[i],)
    temp = query_rec(point,INPUT_EVENT_COUNT,2)
    return temp

def query_rec(point,k,rk):
    # KNN SEARCH... TOO MUCH CODE AND DOESNT MATTER FOR THE QUESTION

sorted_training_data = [t for t in pool.imap(get_neighbors,np.arange(num_points) if t]

我想要实现的目标:

我想重构get_neighborsquery_rec以不使用全局变量,但仍然可以使用多重处理。

我尝试的第1部分:

我重构了上述函数,以便将全局变量作为参数:

def get_neighbors(points,tree,i,k=INPUT_EVENT_COUNT):
    point = points[i]
    temp = query_rec(point,2)
    return temp

在此之后,我必须进行迭代,以包含要传递给新重构的函数的所有参数:

pool = Pool(NUM_WORKERS)
args = zip([points] * num_points,[training_tree] * num_points,np.arange(num_points))
sorted_training_data = [t for t in pool.starmap(get_neighbors,args) if t]

我的解决方案存在的问题points中大约有300万个点,而我正在复制300万个KDTree training_tree。这对我来说真的很糟糕。

我尝试过的第二部分:

我尝试将所需的功能封装在新的数据结构中,如下所示:

class TimeTree:
    """
    A data structure combining a KDTree and a uniform gridspace of points for efficeient NN searches.
    """
    def __init__(self,kdtree,grid_points):
        """
        :param kdtree: a KDTree containing event data points in the form (lat,lng,time)
        :param grid_points: A uniform gridspace of (lat,time) points 
        """
        self.tree = kdtree
        self.points = grid_points
        self.size = len(grid_points)
        
    def search(self,idx,rk=2):
        """
        A function designed to be used with a multiprocess.pool to perform a global KNN search
        of all points in the ``self.points`` list.
        :param idx: The index of the point to search around.
        :param k: The number of neighbors to search for.
        :param rk: A recursive constant for extended search capabilites.
        """
        return query_rec(self.points[idx],self.tree,rk)      

然后创建一个辅助函数生成数据:

def generate_data(k,t,workers=NUM_WORKERS):
    args = zip(np.arange(t.size),[k] * t.size)
    with Pool(workers) as p:
        data = [d for d in tqdm(p.starmap(t.search,args),total=t.size) if d]
    return data

我读到这是对使用Pool时出现泡菜问题的Pool.map个对象的解决方案。我相信这可能行得通,除了在query_rec定义中发现了另一个我之前从未注意到的全局变量。这可能是一个解决方案,稍后我将进行更新。

问题:

如何在以大型数据结构作为参数的函数上有效地使用多重处理?

解决方法

实际上,我建议您使用functools中的部分模块,这对我有很大帮助:

from functools import partial

def f(x,y,a,b,c,d):
  # important: abcd variables should be at the right
  return x + y + a + b + c + d
    
f_of_x = partial(f,a=5,b=10,c=15,d=20) 

您可以将此 f_of_x 传递给Pool starmap(f_of_x,zip(X,Y))

您还可以从另一个带有常量的文件中导入 abcd 的所有值。但请注意:带有导入的全局变量的池将不会在原始文件中更新或更改(如果执行)(它的工作方式很怪异),也不会腌制lambda。

实际上,您可能会遇到许多与Pool和Python中的其他多处理相关的问题,有时很难找到答案。其中一些可以通过其他或相关的Google查询找到,祝您好运:)