资源#
Ray 使您无需更改代码即可将应用程序从笔记本电脑无缝扩展到集群。**Ray 资源**是实现此功能的关键。它们抽象了物理机器,让您能够用资源来表达您的计算,而系统则根据资源请求来管理调度和自动扩缩容。
Ray 中的资源是一个键值对,其中键表示资源名称,值表示浮点数量。为方便起见,Ray 原生支持 CPU、GPU 和内存资源类型;CPU、GPU 和内存被称为**预定义资源**。此外,Ray 还支持自定义资源。
物理资源和逻辑资源#
物理资源是机器物理上拥有的资源,例如物理 CPU 和 GPU,而逻辑资源是系统定义的虚拟资源。
Ray 资源是**逻辑**的,不需要与物理资源有一一对应的映射。例如,您可以通过 ray start --head --num-cpus=0
启动一个逻辑 CPU 为 0 的 Ray 头节点,即使它物理上有 8 个(这向 Ray 调度器发出信号,使其不在头节点上调度需要逻辑 CPU 资源的任何任务或 actor,主要是为了保留头节点运行 Ray 系统进程)。它们主要用于调度过程中的准入控制。
资源是逻辑的这一事实有几个含义
任务或 actor 的资源需求不**强制限制实际物理资源使用**。例如,Ray 不会阻止一个
num_cpus=1
的任务启动多个线程并使用多个物理 CPU。您有责任确保任务或 actor 使用的资源不超过资源需求中指定的数量。Ray 不提供任务或 actor 的 CPU 隔离。例如,Ray 不会专门保留一个物理 CPU 并将一个
num_cpus=1
的任务固定到它上面。相反,Ray 会让操作系统来调度和运行任务。如果需要,您可以使用操作系统 API,如sched_setaffinity
,将任务固定到物理 CPU。Ray 通过自动设置
CUDA_VISIBLE_DEVICES
环境变量来提供 可见设备 形式的 GPU 隔离,大多数 ML 框架都会遵守此变量以进行 GPU 分配。
注意
如果通过 ray.remote()
和 task.options()
/actor.options()
在任务/actor 上设置了 num_cpus
,Ray 会设置环境变量 OMP_NUM_THREADS=<num_cpus>
。如果未指定 num_cpus
,Ray 会设置 OMP_NUM_THREADS=1
;这样做是为了避免在许多 worker 情况下出现性能下降(问题 #6998)。您也可以通过显式设置 OMP_NUM_THREADS
来覆盖 Ray 默认设置的任何值。 OMP_NUM_THREADS
在 numpy、PyTorch 和 Tensorflow 中常用于执行多线程线性代数。在多 worker 设置中,我们希望每个 worker 有一个线程而不是每个 worker 有许多线程,以避免争用。其他一些库可能有自己的并行配置方式。例如,如果您使用 OpenCV,则应使用 `cv2.setNumThreads(num_threads)` 手动设置线程数(设置为 0 以禁用多线程)。
物理资源 vs 逻辑资源#
自定义资源#
除了预定义资源外,您还可以指定 Ray 节点的自定义资源并在任务或 actor 中请求它们。自定义资源的一些使用案例:
您的节点具有特殊硬件,您可以将其表示为自定义资源。然后您的任务或 actor 可以通过
@ray.remote(resources={"special_hardware": 1})
请求该自定义资源,Ray 会将任务或 actor 调度到具有该自定义资源的节点上。您可以使用自定义资源作为标签来标记节点,从而实现基于标签的亲和性调度。例如,您可以通过
ray.remote(resources={"custom_label": 0.001})
将任务或 actor 调度到具有custom_label
自定义资源的节点。对于此使用案例,实际数量并不重要,约定是指定一个很小的数字,这样标签资源就不会成为并行度的限制因素。
指定节点资源#
默认情况下,Ray 节点会启动预定义的 CPU、GPU 和内存资源。每个节点上这些逻辑资源的数量会被设置为 Ray 自动检测到的物理数量。默认情况下,逻辑资源根据以下规则配置。
警告
Ray 不允许在节点上启动 Ray 后动态更新资源容量。
逻辑 CPU 数量 (``num_cpus``):设置为机器/容器的 CPU 数量。
逻辑 GPU 数量 (``num_gpus``):设置为机器/容器的 GPU 数量。
内存 (``memory``):在 Ray 运行时启动时设置为“可用内存”的 70%。
对象存储内存 (``object_store_memory``):在 Ray 运行时启动时设置为“可用内存”的 30%。请注意,对象存储内存不是逻辑资源,用户不能将其用于调度。
然而,您始终可以通过手动指定预定义资源的数量和添加自定义资源来覆盖默认设置。根据您启动 Ray 集群的方式,有几种方法可以做到这一点:
如果您正在使用 ray.init()
启动单节点 Ray 集群,您可以执行以下操作来手动指定节点资源:
# This will start a Ray node with 3 logical cpus, 4 logical gpus,
# 1 special_hardware resource and 1 custom_label resource.
ray.init(num_cpus=3, num_gpus=4, resources={"special_hardware": 1, "custom_label": 1})
如果您正在使用 ray start 启动 Ray 节点,您可以运行:
ray start --head --num-cpus=3 --num-gpus=4 --resources='{"special_hardware": 1, "custom_label": 1}'
如果您正在使用 ray up 启动 Ray 集群,您可以在 yaml 文件中设置 resources 字段:
available_node_types:
head:
...
resources:
CPU: 3
GPU: 4
special_hardware: 1
custom_label: 1
如果您正在使用 KubeRay 启动 Ray 集群,您可以在 yaml 文件中设置 rayStartParams 字段:
headGroupSpec:
rayStartParams:
num-cpus: "3"
num-gpus: "4"
resources: '"{\"special_hardware\": 1, \"custom_label\": 1}"'
指定任务或 Actor 资源需求#
Ray 允许指定任务或 actor 的逻辑资源需求(例如 CPU、GPU 和自定义资源)。任务或 actor 只有在节点上有足够的所需逻辑资源可用时才会运行。
默认情况下,Ray 任务使用 1 个逻辑 CPU 资源,Ray actor 使用 1 个逻辑 CPU 用于调度,0 个逻辑 CPU 用于运行。(这意味着,默认情况下,actor 不能在零 CPU 节点上调度,但无限数量的 actor 可以在任何非零 CPU 节点上运行。actor 的默认资源需求是出于历史原因选择的。建议始终明确设置 actor 的 num_cpus
以避免任何意外。如果明确指定资源,它们在调度时和执行时都是必需的。)
您也可以通过 ray.remote()
和 task.options()
/actor.options()
明确指定任务或 actor 的逻辑资源需求(例如,一个任务可能需要一个 GPU),而不是使用默认需求。
# Specify the default resource requirements for this remote function.
@ray.remote(num_cpus=2, num_gpus=2, resources={"special_hardware": 1})
def func():
return 1
# You can override the default resource requirements.
func.options(num_cpus=3, num_gpus=1, resources={"special_hardware": 0}).remote()
@ray.remote(num_cpus=0, num_gpus=1)
class Actor:
pass
# You can override the default resource requirements for actors as well.
actor = Actor.options(num_cpus=1, num_gpus=0).remote()
// Specify required resources.
Ray.task(MyRayApp::myFunction).setResource("CPU", 1.0).setResource("GPU", 1.0).setResource("special_hardware", 1.0).remote();
Ray.actor(Counter::new).setResource("CPU", 2.0).setResource("GPU", 1.0).remote();
// Specify required resources.
ray::Task(MyFunction).SetResource("CPU", 1.0).SetResource("GPU", 1.0).SetResource("special_hardware", 1.0).Remote();
ray::Actor(CreateCounter).SetResource("CPU", 2.0).SetResource("GPU", 1.0).Remote();
任务和 actor 的资源需求对 Ray 的调度并发性有影响。特别是,给定节点上所有并发执行的任务和 actor 的逻辑资源需求总和不能超过该节点的总逻辑资源。此属性可用于限制并发运行的任务或 actor 数量,以避免出现 OOM 等问题。
小数资源需求#
Ray 支持小数资源需求。例如,如果您的任务或 actor 是 IO 密集型且 CPU 使用率较低,您可以指定小数 CPU 数量 num_cpus=0.5
,甚至零 CPU num_cpus=0
。小数资源需求的精度为 0.0001,因此您应该避免指定超出此精度的双精度浮点数。
@ray.remote(num_cpus=0.5)
def io_bound_task():
import time
time.sleep(1)
return 2
io_bound_task.remote()
@ray.remote(num_gpus=0.5)
class IOActor:
def ping(self):
import os
print(f"CUDA_VISIBLE_DEVICES: {os.environ['CUDA_VISIBLE_DEVICES']}")
# Two actors can share the same GPU.
io_actor1 = IOActor.remote()
io_actor2 = IOActor.remote()
ray.get(io_actor1.ping.remote())
ray.get(io_actor2.ping.remote())
# Output:
# (IOActor pid=96328) CUDA_VISIBLE_DEVICES: 1
# (IOActor pid=96329) CUDA_VISIBLE_DEVICES: 1
注意
GPU、TPU 和 neuron_cores 资源需求大于 1 时,必须是整数。例如,`num_gpus=1.5` 是无效的。
提示
除了资源需求,您还可以为任务或 actor 指定运行环境,其中可以包括 Python 包、本地文件、环境变量等。有关详细信息,请参阅运行时环境。