大模型推理服务的请求队列与优先级调度:从先到先得到智能排序,推理资源的效率最大化
大模型推理服务的请求队列与优先级调度:从先到先得到智能排序,推理资源的效率最大化

一、推理服务的排队困境:GPU 资源的稀缺与请求的洪峰
大模型推理服务面临一个核心矛盾:GPU 资源昂贵且有限,但推理请求的到达模式是突发性的。在流量高峰期,请求队列迅速积压,所有请求按 FIFO 顺序处理,导致高优先级请求(如付费用户、实时对话)与低优先级请求(如批量处理、离线分析)同等对待。付费用户等待 10 秒才收到响应,而批量任务占用了大量 GPU 时间。
更深层的问题是,不同请求的"价值"和"紧急度"不同。一个实时对话请求如果延迟超过 2 秒,用户体验就会严重下降;而一个文档摘要请求延迟 30 秒也是可接受的。FIFO 调度无法区分这些差异,导致 GPU 资源在低价值请求上浪费,高价值请求却得不到及时处理。
二、优先级调度的架构与队列模型
flowchart TD
A[推理请求入口] --> B[请求分类器]
B --> B1[实时对话: P0 最高优先级]
B --> B2[API 调用: P1 高优先级]
B --> B3[批量任务: P2 低优先级]
B --> B4[离线分析: P3 最低优先级]
B1 --> C[多级优先级队列]
B2 --> C
B3 --> C
B4 --> C
C --> D[调度器]
D --> D1[优先级抢占: P0 可抢占 P2/P3]
D --> D2[权重分配: P1:P2:P3 = 5:3:2]
D --> D3[饥饿防护: 低优先级保底配额]
D --> E[GPU 推理引擎]
E --> F[结果返回]
2.1 多级优先级队列
# priority_scheduler.py — 推理服务优先级调度器
# 设计意图:实现多级优先级队列,支持优先级抢占和饥饿防护,
# 确保 GPU 资源优先服务于高价值请求
import asyncio
import time
from dataclasses import dataclass, field
from enum import IntEnum
from typing import Any, Callable, Awaitable
class Priority(IntEnum):
REALTIME = 0 # 实时对话,延迟敏感
API_CALL = 1 # API 调用,中等延迟
BATCH = 2 # 批量任务,延迟不敏感
OFFLINE = 3 # 离线分析,可长时间等待
@dataclass(order=True)
class InferenceRequest:
priority: int = field(compare=True)
enqueue_time: float = field(compare=True)
request_id: str = field(compare=False)
prompt: str = field(compare=False)
max_tokens: int = field(compare=False)
user_tier: str = field(compare=False)
callback: Callable[[Any], Awaitable[None]] = field(compare=False, default=None)
class PriorityScheduler:
def __init__(self, max_concurrent: int = 4):
self.queues: dict[Priority, list[InferenceRequest]] = {
p: [] for p in Priority
}
self.max_concurrent = max_concurrent
self.active_count = 0
self._lock = asyncio.Lock()
self._condition = asyncio.Condition(self._lock)
# 饥饿防护:每个优先级的最小处理配额
self.quota = {Priority.REALTIME: 0, Priority.API_CALL: 0,
Priority.BATCH: 0, Priority.OFFLINE: 0}
self.quota_reset_interval = 100 # 每100个请求重置配额
self.processed_count = 0
async def enqueue(self, request: InferenceRequest) -> None:
"""将请求加入对应优先级队列"""
async with self._lock:
self.queues[Priority(request.priority)].append(request)
self._condition.notify_all()
async def dequeue(self) -> InferenceRequest | None:
"""按优先级策略取出下一个请求"""
async with self._condition:
while self._all_empty():
await self._condition.wait()
# 饥饿防护检查:如果低优先级等待过久,提升其配额
self._check_starvation()
# 按优先级顺序查找可用请求
for priority in Priority:
queue = self.queues[priority]
# 检查该优先级是否有配额(饥饿防护可能限制高优先级)
if queue and self.quota[priority] >= 0:
request = queue.pop(0)
self.processed_count += 1
if self.processed_count >= self.quota_reset_interval:
self._reset_quota()
return request
return None
def _all_empty(self) -> bool:
return all(len(q) == 0 for q in self.queues.values())
def _check_starvation(self) -> None:
"""检测低优先级是否被饥饿"""
now = time.time()
for priority in [Priority.BATCH, Priority.OFFLINE]:
queue = self.queues[priority]
if queue and now - queue[0].enqueue_time > 30:
# 低优先级等待超过30秒,限制高优先级配额
self.quota[Priority.REALTIME] = max(self.quota[Priority.REALTIME] - 5, 0)
self.quota[priority] += 5
def _reset_quota(self) -> None:
"""重置配额计数"""
self.quota = {p: 0 for p in Priority}
self.processed_count = 0
async def process_loop(self, inference_engine: Callable) -> None:
"""调度主循环"""
while True:
if self.active_count >= self.max_concurrent:
await asyncio.sleep(0.1)
continue
request = await self.dequeue()
if request is None:
continue
self.active_count += 1
asyncio.create_task(self._execute_request(request, inference_engine))
async def _execute_request(
self,
request: InferenceRequest,
inference_engine: Callable,
) -> None:
"""执行推理请求"""
try:
result = await inference_engine(request.prompt, request.max_tokens)
if request.callback:
await request.callback(result)
except Exception as e:
if request.callback:
await request.callback({"error": str(e)})
finally:
self.active_count -= 1
2.2 动态优先级调整
# dynamic_priority.py — 动态优先级调整策略
# 设计意图:根据请求的等待时间和用户等级动态调整优先级,
# 避免低优先级请求被无限期饥饿
import time
class DynamicPriorityAdjuster:
# 等待时间阈值(秒):超过此时间提升优先级
WAIT_THRESHOLDS = {
0: float('inf'), # P0 不需要提升
1: 10, # P1 等待10秒后提升
2: 30, # P2 等待30秒后提升
3: 60, # P3 等待60秒后提升
}
# 用户等级优先级加成
TIER_BOOST = {
"enterprise": -1, # 企业用户提升一级
"pro": 0, # 专业用户不变
"free": 1, # 免费用户降低一级
}
@classmethod
def adjust_priority(cls, request: InferenceRequest) -> int:
"""动态调整请求优先级"""
base_priority = request.priority
wait_time = time.time() - request.enqueue_time
# 等待时间提升
threshold = cls.WAIT_THRESHOLDS.get(base_priority, float('inf'))
if wait_time > threshold:
base_priority = max(0, base_priority - 1)
# 用户等级加成
tier_boost = cls.TIER_BOOST.get(request.user_tier, 0)
adjusted = max(0, min(3, base_priority + tier_boost))
return adjusted
三、批量推理与请求合并
3.1 请求合并策略
# batch_merger.py — 推理请求批量合并
# 设计意图:将多个短请求合并为一个 Batch 推理,
// 提升 GPU 利用率,降低单请求延迟
import asyncio
import time
from dataclasses import dataclass
@dataclass
class BatchGroup:
requests: list[InferenceRequest]
created_at: float
total_tokens: int
class BatchMerger:
def __init__(
self,
max_batch_size: int = 8,
max_wait_ms: int = 50,
max_total_tokens: int = 4096,
):
self.max_batch_size = max_batch_size
self.max_wait_ms = max_wait_ms
self.max_total_tokens = max_total_tokens
self.pending: list[InferenceRequest] = []
self._lock = asyncio.Lock()
async def add_request(self, request: InferenceRequest) -> BatchGroup | None:
"""添加请求到待合并队列,满足条件时返回一个 Batch"""
async with self._lock:
self.pending.append(request)
# 检查是否满足批量条件
if len(self.pending) >= self.max_batch_size:
return self._create_batch()
if sum(r.max_tokens for r in self.pending) >= self.max_total_tokens:
return self._create_batch()
return None
async def wait_and_flush(self) -> BatchGroup | None:
"""等待超时后刷新当前待合并队列"""
await asyncio.sleep(self.max_wait_ms / 1000)
async with self._lock:
if self.pending:
return self._create_batch()
return None
def _create_batch(self) -> BatchGroup:
batch = BatchGroup(
requests=self.pending[:],
created_at=time.time(),
total_tokens=sum(r.max_tokens for r in self.pending),
)
self.pending = []
return batch
四、边界分析与架构权衡
优先级抢占的实现复杂度:真正的优先级抢占需要中断正在执行的推理任务,但 GPU 推理一旦开始就无法中断。实际实现中,"抢占"只能发生在请求调度阶段——高优先级请求跳过队列直接进入执行,而非中断正在执行的请求。这意味着抢占的响应延迟取决于当前推理任务的执行时间。
饥饿防护与优先级的矛盾:饥饿防护机制为低优先级请求保留配额,但这会牺牲高优先级请求的响应速度。在极端流量场景下,保护低优先级可能导致付费用户体验下降。需要在商业策略和技术实现之间找到平衡。
批量合并的延迟权衡:请求合并需要等待队列积累到一定数量,这引入了额外的等待延迟。对于实时对话场景,即使 50ms 的等待也是不可接受的。解决方案是对实时请求跳过合并,直接执行;只对批量任务和离线分析进行合并。
队列状态的监控与可观测性:多级优先级队列的运行状态需要实时监控——各队列的积压长度、平均等待时间、调度器的吞吐量。缺乏可观测性会导致问题难以定位。建议暴露 Prometheus 指标并配置告警。
五、总结
推理服务的优先级调度将 GPU 资源分配从"先到先得"升级为"价值驱动",确保高价值请求优先获得推理资源。多级优先级队列、动态优先级调整和请求批量合并三个机制组合,可以在保证付费用户体验的同时最大化 GPU 利用率。但抢占限制、饥饿防护、合并延迟和可观测性是需要持续关注的边界条件。落地建议:按用户等级和请求类型划分优先级;实时请求跳过合并直接执行;暴露队列指标并配置积压告警;定期审查优先级策略与商业目标的匹配度。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)