Python 调用dask Client.map()时会发生什么?

Python 调用dask Client.map()时会发生什么?,python,mapreduce,dask,Python,Mapreduce,Dask,我正在尝试使用dask编写一个网格搜索实用程序。目标函数调用包含大量数据的类的方法。我试图使用dask将计算并行化到多核心解决方案,而不必复制原始类/数据帧。我在文档中没有找到任何解决方案,因此我在这里发布了一个玩具示例: import pickle from dask.distributed import Client, LocalCluster from multiprocessing import current_process class TestClass: def __i

我正在尝试使用dask编写一个网格搜索实用程序。目标函数调用包含大量数据的类的方法。我试图使用dask将计算并行化到多核心解决方案,而不必复制原始类/数据帧。我在文档中没有找到任何解决方案,因此我在这里发布了一个玩具示例:

import pickle
from dask.distributed import Client, LocalCluster
from multiprocessing import current_process


class TestClass:
    def __init__(self):
        self.param = 0

    def __getstate__(self):
        print("I am pickled!")
        return self.__dict__

    def loss(self, ext_param):
        self.param += 1
        print(f"{current_process().pid}: {hex(id(self))}:  {self.param}: {ext_param} ")
        return f"{self.param}_{ext_param}"


def objective_function(param):
    return test_instance.loss(param)

if __name__ == '__main__':

    test_instance = TestClass()
    print(hex(id(test_instance)))
    cluster = LocalCluster(n_workers=2)
    client = Client(cluster)
    futures = client.map(objective_function, range(20))
    result = client.gather(futures)
    print(result)
    
# ---- OUTPUT RESULTS ----
# 0x7fe0a5056d30
# I am pickled!
# I am pickled!
# 11347: 0x7fb9bcfa0588:  1: 0
# 11348: 0x7fb9bd0a2588:  1: 1
# 11347: 0x7fb9bcf94240:  1: 2
# 11348: 0x7fb9bd07b6a0:  1: 3
# 11347: 0x7fb9bcf945f8:  1: 4 
# ['1_0', '1_1', '1_2', '1_3', '1_4']
我有以下问题:

  • 为什么要调用下面的pickle函数两次
  • 我注意到map函数的每一次迭代都使用了一个新的
    test\u实例副本
    ,从每次迭代的不同类地址以及
    test\u实例.param
    属性在每次迭代时都被设置为0这一事实可以看出这一点(这种行为不同于我强调的多处理池的标准实现)。我假设在每次迭代过程中,每个进程都会收到一个新的pickle类副本,对吗
  • 从(2)开始,计算期间内存中有多少个
    test_instance
    副本?是否为1(对于主线程中的原始实例)+1(pickle copy)+2(每个进程中存在的实例)=4?是否有办法将该值设为1
  • 我注意到,一些共享内存解决方案可以通过使用中建议的Ray library获得

    为什么要调用下面的pickle函数两次

    通常,python的pickle会有效地将实例变量和对导入模块中类的引用捆绑在一起。在
    \uuuu main\uuuu
    中,这可能不可靠,dask会退回到cloudpickle(它在内部也调用pickle)。在我看来,这就像检查是否在
    中一样
    distributed.protocol.pickle.dumps中的
    可能在首次尝试pickle之前发生

    在每次迭代过程中,每个进程都将收到一个新的pickle类副本

    是的。每次dask运行任务时,它都会对输入进行反序列化,从而创建实例的nw副本。请注意,您的dask工作线程可能是通过fork_服务器技术创建的,因此不会简单地复制内存(这是安全的方法)

    您可以在计算之前将实例“分散”给工作人员,他们可以重用其本地副本,但dask任务不应该通过改变对象而工作,而是通过返回结果(即功能上的)

    内存中有多少个test_实例副本

    客户端中为1,每个正在执行的任务加上一个。序列化版本也可能存在,可能一个保存在图形中,临时保存在客户端上,然后保存在计划程序上;在反序列化时,它也将临时保存在工作内存中。对于某些类型,零拷贝de/ser是可能的

    如果由于对象的大小,任务非常大,那么您肯定应该事先“分散”它们(
    client.scatter

    有没有办法把这个值设为1

    您可以运行调度程序和/或进程中的工作者来共享内存,但是,当然,这样您就失去了与GIL的并行性


    也许你可以试试?该模式似乎与你的工作流程相匹配。

    谢谢,这是一个很好的答案。你能给我指一些关于fork_服务器的信息吗?它与os.fork有什么不同吗?是的,不同: