反模式:在循环中调用 ray.get 会损害并行性#

TLDR: 避免在循环中调用 ray.get(),因为它是一个阻塞调用;只在获取最终结果时使用 ray.get()

调用 ray.get() 会获取远程执行函数的结果。然而,它是一个阻塞调用,这意味着它总是等待直到请求的结果可用。如果您在循环中调用 ray.get(),循环将不会继续运行,直到 ray.get() 调用被解决。如果您也在同一个循环中生成远程函数调用,最终将完全没有并行性,因为您等待上一个函数调用完成(由于 ray.get()),然后在循环的下一次迭代中才生成下一个调用。这里的解决方案是将 ray.get() 的调用与远程函数的调用分开。这样,所有远程函数都在等待结果之前生成,并可以在后台并行运行。此外,您可以向 ray.get() 传递一个对象引用列表,而不是一个接一个地调用它来等待所有任务完成。

如果你也在同一个循环中启动远程函数调用,你最终将完全没有并行性,因为你会等待上一个函数调用完成 (由于 ray.get()) 并且只在循环的下一个迭代中启动下一个调用。这里的解决方案是将 ray.get() 的调用与远程函数的调用分开。这样一来,所有远程函数都会在我们等待结果之前被启动,并且可以在后台并行运行。此外,你可以将一个对象引用列表传递给 ray.get(),而不是一个接一个地调用它,来等待所有任务完成。

代码示例#

import ray

ray.init()


@ray.remote
def f(i):
    return i


# Anti-pattern: no parallelism due to calling ray.get inside of the loop.
sequential_returns = []
for i in range(100):
    sequential_returns.append(ray.get(f.remote(i)))

# Better approach: parallelism because the tasks are executed in parallel.
refs = []
for i in range(100):
    refs.append(f.remote(i))

parallel_returns = ray.get(refs)
../../_images/ray-get-loop.svg

在循环中调用 ray.get()#

在调度远程工作后立即调用 ray.get() 时,循环会阻塞直到接收到结果。因此,我们最终进行的是顺序处理。相反,我们应该首先调度所有远程调用,然后这些调用会并行处理。调度工作后,我们可以一次性请求所有结果。

其他与 ray.get() 相关的反模式包括