API 设计
阅读 Mini Ray 预览版前,先看 API。这里要保留 Ray 的基本语义,而不是只暴露一个线程池。
顶层 API
Mini Ray 暴露这些函数:
python
ray.init(num_workers=4)
ray.shutdown()
ray.remote(fn_or_class)
ray.put(value)
ray.get(ref_or_refs)
ray.wait(refs, num_returns=1, timeout=None)
ray.kill(actor)
ray.metrics()这组 API 覆盖了 Ray Core 的最小学习面。
remote 同时支持函数和类
函数:
python
@ray.remote
def add(a, b):
return a + b
ref = add.remote(1, 2)类:
python
@ray.remote
class Counter:
...
counter = Counter.remote()
ref = counter.incr.remote()因此 remote 需要判断传入对象类型:
python
def remote(obj=None, **options):
if obj is None:
return lambda actual: remote(actual, **options)
if inspect.isclass(obj):
return RemoteActorClass(obj, options)
if callable(obj):
return RemoteFunction(obj, options)
raise TypeError(...)为什么不直接返回 Future
Python 标准库已经有 concurrent.futures.Future。Mini Ray 仍然定义 ObjectRef,是为了贴近 Ray 的语义:
ObjectRef表示运行时对象,不只是线程结果。- 它可以作为另一个任务的参数。
- 它可以由
put创建,也可以由 Task 或 Actor 方法创建。 - 它把对象存储和任务依赖连接起来。
python
@dataclass(frozen=True)
class ObjectRef:
object_id: str这个 ID 是对象系统的核心句柄。
资源选项
Mini Ray 支持:
python
@ray.remote(num_cpus=2)
def cpu_task():
...
@ray.remote(num_cpus=1, num_gpus=1)
def gpu_task():
...
@ray.remote(resources={"ssd": 1})
def io_task():
...包装对象保留 options:
python
class RemoteFunction:
def __init__(self, fn, options=None):
self._fn = fn
self._options = dict(options or {})
def options(self, **options):
merged = dict(self._options)
merged.update(options)
return RemoteFunction(self._fn, merged)这样支持 Ray 常见写法:
python
ref = work.options(num_cpus=2).remote(item)API 和实现的分界
API 层只负责把用户意图变成运行时请求:
| API 层对象 | 运行时请求 |
|---|---|
RemoteFunction.remote() | Runtime.submit_task(...) |
RemoteActorClass.remote() | Runtime.create_actor(...) |
ActorMethod.remote() | Runtime.submit_actor_method(...) |
ray.get() | ObjectStore.get(...) |
ray.wait() | ObjectStore.wait(...) |
这个分界和真实 Ray 的 Python 层 / Core Worker 分界非常像。
小练习
给 Mini Ray 增加一个 ray.is_initialized():
python
def is_initialized():
return _runtime is not None再思考:真实 Ray 为什么还要处理地址、命名空间、runtime_env、Job ID 等信息,而 Mini Ray 不需要?