在IPython Parallel中处理无法序列化的尴尬并行任务(或其他包)

3 投票
2 回答
720 浏览
提问于 2025-04-17 18:01

我经常遇到这样的问题:我想快速对一大堆对象做一些简单的操作。我的自然选择是使用 IPython Parallel,因为它简单易用,但我常常要处理一些无法被序列化的对象。经过几个小时的尝试后,我通常只能选择在一台电脑上过夜运行我的任务,或者做一些傻事,比如把事情半手动地分开,去运行多个 Python 脚本。

举个具体的例子,假设我想删除某个 S3 存储桶里的所有键。

我通常会不假思索地这样做:

import boto
from IPython.parallel import Client

connection = boto.connect_s3(awskey, awssec)
bucket = connection.get_bucket('mybucket')

client = Client()
loadbalancer = c.load_balanced_view()

keyList = list(bucket.list())
loadbalancer.map(lambda key: key.delete(), keyList)

问题是,boto 中的 Key 对象是无法被序列化的 (*). 这种情况在我不同的场景中经常发生。这也是我在使用多进程、execnet 以及我尝试过的其他框架和库时遇到的问题(原因很明显:它们都使用同样的序列化工具来处理对象)。

你们也有这样的烦恼吗?有没有办法让我序列化这些更复杂的对象?我需要为这些特定的对象写一个自己的序列化工具吗?如果需要,我该如何告诉 IPython Parallel 使用它?我该如何编写一个序列化工具?

谢谢!


(*) 我知道我可以简单地列出键的名称,然后这样做:

loadbalancer.map(lambda keyname: getKey(keyname).delete())

并在 IPython 集群的每个引擎中定义 getKey 函数。这只是我经常遇到的一个更普遍问题的一个特例。也许这个例子不太好,因为它可以用其他方法轻松解决。

2 个回答

0

IPython真是让大家聚在一起啊;)。根据我了解到的情况,问题出在对象的方法上。也就是说,或许你可以不直接用key的方法来删除它,而是写一个函数,传入这个键,然后删除它。你可以先获取一个包含每个键相关信息的dict列表,然后再调用一个函数delete_key( dict ),这个函数的具体实现就留给你自己去写,因为我对处理s3键没什么头绪。

这样行得通吗?


另外一种可能是这样做:与其调用实例的方法,不如用实例作为参数来调用类的方法。所以,不用lambda key : key.delete(),而是用lambda key : Key.delete(key)。当然,你得把类推送到节点上,但这应该不成问题。下面是一个简单的例子:

 class stuff(object):
       def __init__(self,a=1):
            self.list = []
       def append(self, a):
            self.list.append(a)

  import IPython.parallel as p
  c = p.Client()
  dview = c[:]

  li = map( stuff, [[]]*10 ) # creates 10 stuff instances

  dview.map( lambda x : x.append(1), li ) # should append 1 to all lists, but fails

  dview.push({'stuff':stuff}) # push the class to the engines
  dview.map( lambda x : stuff.append(x,1), li ) # this works.
2

IPython 有一个 use_dill 选项,如果你安装了 dill 这个工具,你就可以把大部分“无法被序列化”的对象进行序列化。

我怎么能在负载均衡视图中使用 dill 而不是 pickle

撰写回答