性能调优#
本节将帮助您
了解 Ray Serve 的性能特点
找到调试和调优 Serve 应用程序性能的方法
注意
本节提供了一些提高 Ray Serve 应用程序性能的技巧和窍门。请参阅 架构页面 以获取有帮助的背景信息,包括 HTTP 代理 Actor 和部署副本 Actor 的概述。
目录
性能和基准测试#
Ray Serve 构建在 Ray 之上,因此其可伸缩性受限于 Ray 的可伸缩性。请参阅 Ray 的 可伸缩性包络,以了解有关节点数量和其他限制的更多信息。
调试请求路径中的性能问题#
您最可能遇到的性能问题是请求延迟高或吞吐量低。
设置好 Ray 和 Ray Serve 的 监控 后,这些问题可能会表现为:
serve_num_router_requests_total保持不变,而您的负载在增加serve_deployment_processing_latency_ms飙升,因为查询在后台排队
以下是解决这些问题的方法:
确保您使用的是正确的硬件和资源
您是否使用
ray_actor_options为您的部署副本预留 GPU(例如,ray_actor_options={“num_gpus”: 1})?您是否使用
ray_actor_options为您的部署副本预留一个或多个 CPU 核心(例如,ray_actor_options={“num_cpus”: 2})?您是否设置了 OMP_NUM_THREADS 来提高深度学习框架的性能?
尝试批处理您的请求。请参阅 动态请求批处理。
考虑在您的可调用对象中使用
async方法。请参阅 下面的部分。为 HTTP 请求设置端到端超时。请参阅 下面的部分。
使用 async 方法#
注意
根据 FastAPI 文档,def 端点函数在单独的线程池中调用,因此您可能会观察到在一个副本中同时运行许多请求,这种情况可能会导致 OOM 或资源耗尽。在这种情况下,您可以尝试使用 async def 来控制工作负载的性能。
您的可调用对象是否使用了 async def?如果您正在使用 asyncio 并遇到上述相同的排队问题,您可能需要增加 max_ongoing_requests。默认情况下,Serve 将此值设置为一个较低的值(5),以确保客户端接收到适当的反压。您可以在部署装饰器中增加此值;例如,@serve.deployment(max_ongoing_requests=1000)。
设置端到端请求超时#
默认情况下,Serve 让客户端 HTTP 请求完成,无论它们需要多长时间。但是,慢请求可能会成为副本处理的瓶颈,阻塞其他正在等待的请求。设置端到端超时,以便可以终止并重试慢请求。
您可以通过在 Serve 配置的 http_options 字段中设置 request_timeout_s 参数来为 HTTP 请求设置端到端超时。HTTP 代理将在终止 HTTP 请求之前等待那么多秒。此配置对于您的 Ray 集群是全局的,并且您无法在运行时更新它。请使用 客户端重试 来重试因瞬态故障而超时的请求。
注意
当请求超时时,Serve 会返回状态码为 408 的响应。客户端在收到此 408 响应时可以重试。
选择副本时设置回退时间#
Ray Serve 允许您精细调整请求路由器的回退行为,这有助于在等待副本就绪时减少延迟。当重试路由临时不可用的副本时,它使用指数回退策略。您可以通过配置以下环境变量来优化此行为:
RAY_SERVE_ROUTER_RETRY_INITIAL_BACKOFF_S:重试请求之前的初始回退时间(秒)。默认值为0.025。RAY_SERVE_ROUTER_RETRY_BACKOFF_MULTIPLIER:每次重试后应用于回退时间的乘数。默认值为2。RAY_SERVE_ROUTER_RETRY_MAX_BACKOFF_S:重试之间的最大回退时间(秒)。默认值为0.5。
启用吞吐量优化的服务#
注意
在 Ray v2.54.0 中,RAY_SERVE_RUN_USER_CODE_IN_SEPARATE_THREAD 和 RAY_SERVE_RUN_ROUTER_IN_SEPARATE_LOOP 的默认值将更改为 0,以提高性能。
本节详细介绍了如何启用专注于提高吞吐量和降低延迟的 Ray Serve 选项。这些配置侧重于以下方面:
减少与频繁日志记录相关的开销。
禁用允许 Serve 应用程序包含阻塞操作的行为。
如果您的 Ray Serve 代码包含线程阻塞操作,您必须重构代码才能实现更高的吞吐量。下表显示了阻塞和非阻塞代码的示例:
| 阻塞操作 (❌) | 非阻塞操作 (✅) |
|---|---|
from ray import serve
from fastapi import FastAPI
import time
app = FastAPI()
@serve.deployment
@serve.ingress(app)
class BlockingDeployment:
@app.get("/process")
async def process(self):
# ❌ Blocking operation
time.sleep(2)
return {"message": "Processed (blocking)"}
serve.run(BlockingDeployment.bind())
|
from ray import serve
from fastapi import FastAPI
import asyncio
app = FastAPI()
@serve.deployment
@serve.ingress(app)
class NonBlockingDeployment:
@app.get("/process")
async def process(self):
# ✅ Non-blocking operation
await asyncio.sleep(2)
return {"message": "Processed (non-blocking)"}
serve.run(NonBlockingDeployment.bind())
|
要将所有选项配置为推荐设置,请设置环境变量 RAY_SERVE_THROUGHPUT_OPTIMIZED=1。
您也可以单独配置每个选项。下表详细介绍了推荐的配置及其影响:
配置值 |
影响 |
|---|---|
|
您的代码在与副本主事件循环相同的事件循环中运行。您必须避免在请求路径中进行阻塞操作。将此配置设置为 |
|
请求路由器在与您的代码的事件循环相同的事件循环中运行。您必须避免在请求路径中进行阻塞操作。将此配置设置为 |
|
将日志缓冲区设置为每 |
|
仅将日志写入 |
您可能希望在自定义上述选项的同时启用吞吐量优化的服务。您可以通过设置 RAY_SERVE_THROUGHPUT_OPTIMIZED=1 并覆盖特定选项来实现此目的。例如,要启用吞吐量优化的服务并继续将日志记录到 stderr,您应该设置 RAY_SERVE_THROUGHPUT_OPTIMIZED=1 并使用 RAY_SERVE_LOG_TO_STDERR=1 进行覆盖。
调试控制器中的性能问题#
Serve 控制器运行在 Ray 的主节点上,负责各种任务,包括接收来自其他 Ray Serve 组件的自动伸缩指标。如果 Serve 控制器过载(症状可能包括高 CPU 使用率和大量待处理的 ServeController.record_autoscaling_metrics_from_handle 任务),您可以通过设置 RAY_SERVE_CONTROL_LOOP_INTERVAL_S 环境变量(默认为 0.1 秒)来增加控制循环周期之间的间隔。此设置使控制器有更多时间处理请求,并可能有助于缓解过载。