如何有效地链接ipyparallel任务并将中间结果传递给引擎?

时间:2016-06-23 17:45:05

标签: python ipython-parallel

我试图在iPyParallel中将几个任务链接起来,比如

import ipyparallel
client = ipyparallel.Client()
view = client.load_balanced_view()
def task1(x):
    ## Do some work.
    return x * 2
def task2(x):
    ## Do some work.
    return x * 3
def task3(x):
    ## Do some work.
    return x * 4
results1 = view.map_async(task1, [1, 2, 3])
results2 = view.map_async(task2, results1.get())
results3 = view.map_async(task3, results2.get())

但是,除非task1完成且基本上阻止,否则此代码不会提交任何任务2。我的任务可能需要不同的时间,效率非常低。 有没有一种简单的方法可以有效地链接这些步骤,引擎可以从之前的步骤中获得结果?类似于:

def task2(x):
    ## Do some work.
    return x.get() * 3 ## Get AsyncResult out.
def task3(x):
    ## Do some work.
    return x.get() * 4 ## Get AsyncResult out.
results1 = [view.apply_async(task1, x) for x in [1, 2, 3]]
results2 = []
for x in result1:
    view.set_flags(after=x.msg_ids)
    results2.append(view.apply_async(task2, x))
results3 = []
for x in result2:
    view.set_flags(after=x.msg_ids)
    results3.append(view.apply_async(task3, x))

显然,由于AsyncResult不可选,因此会失败。

我正在考虑一些解决方案:

  1. 使用view.map_async(ordered = False)。

    results1 = view.map_async(task1, [1, 2, 3], ordered=False)
    for x in results1:
        results2.append(view.apply_async(task2, x.get()))
    

    但是必须等待所有task1完成才能提交任何task3。它仍在阻挡。

  2. 使用asyncio。

    @asyncio.coroutine
    def submitter(x):
        result1 = yield from asyncio.wrap_future(view.apply_async(task1, x))
        result2 = yield from asyncio.wrap_future(view.apply_async(task2, result1)
        result3 = yield from asyncio.wrap_future(view.apply_async(task3, result2)
        yield result3
    
    @asyncio.coroutine
    def submit_all(ls):
        jobs = [submitter(x) for x in ls]
        results = []
        for async_r in asyncio.as_completed(jobs):
            r = yield from async_r
            results.append(r)
        ## Do some work, like analysing results.
    

    它正在运行,但是当引入更复杂的任务时,代码很快变得混乱和不直观。

  3. 感谢您的帮助。

1 个答案:

答案 0 :(得分:3)

选项1:连锁期货

IPython parallel并不是最好的,因为连接必须在客户端级别完成。在提交结果之前,您必须等待结果完成并返回到客户端。从本质上讲,你的asyncio submit_all是为IPython并行执行它的正确方法。您可以通过编写chain函数来获得更通用的内容,该函数使用add_done_callback在上一个任务完成时提交新任务:

from concurrent.futures import Future
from functools import partial


def chain_apply(view, func, future):
    """Chain a call to view.apply(func, future.result()) when future is ready.

    Returns a Future for the subsequent result.
    """
    f2 = Future()
    # when f1 is ready, submit a new task for func on its result
    def apply_func(f):
        if f.exception():
            f2.set_exception(f.exception())
            return
        print('submitting %s(%s)' % (func.__name__, f.result()))
        ar = view.apply_async(func, f.result())
        # when ar is done, pass through the result to f2
        ar.add_done_callback(lambda ar: f2.set_result(ar.get()))

    future.add_done_callback(apply_func)
    return f2


def chain_map(view, func, list_of_futures):
    """Chain a new callback on a list of futures."""
    return [ chain_apply(view, func, f) for f in list_of_futures ]

# use builtin map with apply, since we want one Future per item
results1 = map(partial(view.apply, task1), [1, 2, 3])
results2 = chain_map(view, task2, results1)
results3 = chain_map(view, task3, results2)
print("Waiting for results")
[ r.result() for r in results3 ]

add_done_callback的任何示例一样,它可以用协同程序编写,但我发现在这种情况下回调很好。这应该至少是一个相当通用的实用程序,您可以使用它来组成管道。

选项2:dask.distributed

完全披露:我是IPython Parallel的主要作者,即将建议您使用其他工具。

可以通过引擎名称空间和IPython并行中的DAG依赖项将结果从一个任务传递到另一个任务,但老实说,如果您的工作流程看起来像这样,您应该考虑使用dask distributed,这是专门为此设计的一种计算图。如果您已经熟悉并且熟悉IPython并行,那么开始使用dask不应该是一个很大的负担。

IPython 5.1提供了一个方便的命令,用于将IPython并行集群转换为dask分布式集群:

import ipyparallel as ipp
client = ipp.Client()
executor = client.become_distributed(ncores=1)

然后,dask的关键相关功能是你可以将future作为参数提交给后续的map调用,并且调度程序在结果准备就绪时会处理它,而不是必须在客户端中显式地执行:

results1 = executor.map(task1, [1, 2, 3])
results2 = executor.map(task2, results1)
results3 = executor.map(task3, results2)
executor.gather(results3)

所以基本上,dask distributed的工作原理是你希望IPython并行的负载平衡能够在你需要链接这样的东西时起作用。

This notebook说明了这两个例子。