Skip to content

实验 1:第一个 Ray Task

这个实验的目标不是写复杂程序,而是观察 Ray 最核心的行为:远程调用立即返回 ObjectRef,真正的结果要通过 ray.get 获取。

准备环境

bash
uv venv
uv pip install -r examples/ray_demos/requirements.txt

运行示例

bash
uv run python examples/ray_demos/01_tasks.py

代码:

python
import time

import ray

ray.init()

@ray.remote
def slow_square(x: int) -> int:
    time.sleep(0.5)
    return x * x

start = time.perf_counter()
refs = [slow_square.remote(i) for i in range(8)]
print(ray.get(refs))
print(f"elapsed={time.perf_counter() - start:.2f}s")

你应该看到什么

结果应该类似:

text
[0, 1, 4, 9, 16, 25, 36, 49]
elapsed=...

耗时通常明显小于 8 * 0.5s,因为 8 个任务不是串行执行。

逐行解释

python
ray.init()

启动或连接 Ray Runtime。单机模式下,它会在本机启动必要组件。

python
@ray.remote
def slow_square(x: int) -> int:
    ...

这一步不是执行函数,而是把函数注册为远程函数。包装后的 slow_square 拥有 .remote() 方法。

python
refs = [slow_square.remote(i) for i in range(8)]

每次 .remote() 都提交一个任务,并立即返回 ObjectRef

python
print(ray.get(refs))

ray.get 等待所有引用 ready,然后返回真实值列表。

串行陷阱

把代码改成这样:

python
results = []
for i in range(8):
    results.append(ray.get(slow_square.remote(i)))

这会慢很多。原因是你每提交一个任务就立刻等待结果,Driver 没有机会把多个任务同时提交给运行时。

和 Mini Ray 对照

运行教学版:

bash
uv run python examples/mini_ray_runtime/demos/01_tasks.py

Mini Ray 的输出也会包含一个 metrics 字典,帮助你看到任务提交数、完成数和对象数。

小练习

  1. time.sleep(0.5) 改成 time.sleep(0.05),观察并行收益是否变小。
  2. 把任务数量从 8 改成 100,观察调度开销和总耗时。
  3. 给远程函数加上 @ray.remote(num_cpus=2),看本机并发数量如何变化。

常见错误

错误原因处理
ModuleNotFoundError: ray没有安装 Ray执行 uv pip install -r examples/ray_demos/requirements.txt
程序没有明显加速任务太小或在循环里 ray.get增大任务粒度,先提交再统一等待
CPU 占用不高任务是 sleep 或 I/O 等待改成 CPU 计算任务观察差异

面向学习目的的 Ray Core 中文导读与 Mini Ray 机制预览。