嵌套的dask.compute没有阻塞

时间:2017-08-09 17:24:38

标签: python dask dask-distributed dask-delayed

dask.compute(...)应该是一个阻塞调用。但是,当我嵌套dask.compute,而内部的I / O(如dask.dataframe.read_parquet)时,内部dask.compute没有阻塞。这是一个伪代码示例:

import dask, distributed

def outer_func(name):
    files = find_files_for_name(name)
    df = inner_func(files).compute()
    # do work with df
    return result

def inner_func(files):
    tasks = [ dask.dataframe.read_parquet(f) for f in files ]
    tasks = dask.dataframe.concat(tasks)
    return tasks

client = distributed.Client(scheduler_file=...)
results = dask.compute([ dask.delay(outer_func)(name) for name in names ])

如果我开始了2名工人,每人有8个流程,例如:

dask-worker --scheduler-file $sched_file --nprocs 8 --nthreads 1

,然后我预计最多运行2 x 8并发inner_func因为inner_func(files).compute()应该是阻塞的。但是,我观察到的是,在一个工作进程中,一旦启动read_parquet步骤,就会有另一个inner_func(文件).compute()开始运行。所以最后可能会有多个inner_func(文件).compute()在运行,有时可能会导致内存不足错误。

这是预期的行为吗?如果是这样,是否可以为每个工作进程强制执行一个inner_func(files).compute()?

2 个答案:

答案 0 :(得分:0)

多进程调度程序似乎不是这种情况。

为了使用分布式调度程序,我通过使用distributed.Client API提交节奏作业而不是依赖于dask.compute来找到解决方法。 dask.compute适用于简单的用例,但显然不知道可以调度多少未完成的任务,因此在这种情况下会超出系统。

这是用于运行带有调步的dask.Delayed任务集合的伪代码:

import distributed as distr

def paced_compute(tasks, batch_size, client):
    """
    Run delayed tasks, maintaining at most batch_size running at any
    time. After the first batch is submitted,
    submit a new job only after an existing one is finished, 
    continue until all tasks are computed and finished.

    tasks: collection of dask.Delayed
    client: distributed.Client obj
    """
    results, tasks = [], list(tasks)
    working_futs = client.compute(tasks[:batch_size])
    tasks = tasks[batch_size:]
    ac = distr.as_completed(working_futs)
    for fut in ac:
        res = fut.result()
        results.append(res)
        if tasks:
            job = tasks.pop()
            ac.add(client.compute(job))
    return results

答案 1 :(得分:0)

当您要求dask分布式调度程序运行时,它会将函数代码和所需的任何数据发送到不同进程(可能在不同计算机上)的工作程序函数。这些工作进程忠实地执行这些函数,运行正常的python代码。关键是,运行函数不知道它是在dask工作器上 - 默认情况下,它会看到没有设置全局dask分布式客户端,并执行dask通常会为此情况执行的操作:执行任何dask默认调度程序(螺纹调度程序)上的工作负载。

如果您确实必须在任务中执行完整的dask-compute操作,并希望这些操作使用运行这些任务的分布式调度程序,则需要使用worker client。但是,我觉得在你的情况下,重新修改作业以删除嵌套(类似于上面的伪代码,虽然这也适用于计算)可能是更简单的方法。