反模式:在循环中调用 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)
在循环中调用 ray.get()
#
在调度远程工作后立即调用 ray.get()
时,循环会阻塞直到接收到结果。因此,我们最终进行的是顺序处理。相反,我们应该首先调度所有远程调用,然后这些调用会并行处理。调度工作后,我们可以一次性请求所有结果。
其他与 ray.get()
相关的反模式包括