清晨六点,一家繁忙的餐厅后厨开始运转。主厨站在中央,面前是堆积如山的订单——有堂食的、外卖的、包间的,还有几桌需要特殊忌口的。每张订单都对应着不同菜品,每道菜都需要不同食材、不同灶台、不同厨师。如果任凭所有人一窝蜂冲向灶台,厨房瞬间就会陷入混乱:锅烧糊了、菜做错了、外卖超时了、客人投诉了。 这个场景,就是 AI 系统运行时每天都在经历的挑战。当你的大模型应用同时接到成百上千个推理请求,当训练任务和线上推理在同一个集群里争抢 GPU,当某个节点突然宕机导致任务中断——谁来当这个“主厨”?谁来制定“配菜顺序”?谁来调度“厨师”和“灶台”? 这套方法,业界叫它 Harness Engineering(缰绳工程)——像给烈马套上缰绳一样,给狂野的 AI 系统套上制度和规则。而其中最核心的两个动作,就是编排(Orchestration)与调度(Scheduling)。编排决定“做什么、按什么顺序做”,调度决定“谁来做、用哪台设备做”。本篇文章,我们就用这个“餐厅后厨”的比喻,把 AI 系统编排与调度的知识完整讲透。
对 AI 从业者而言,掌握编排与调度是搭建高可用、高性能 AI 系统的核心能力。它决定了你的模型在真实流量下是“稳稳输出”,还是“一崩到底”——这是架构师与平台工程师的必备内功,也是从“能跑模型”走向“能管系统”的分水岭。
可视化演示
动态演示AI系统编排与调度的核心机制,通过架构/数据/状态/性能四种模式展示关键概念与交互流程。
本节配有交互动效(进入视口后自动加载,约 3KB)。
AI系统编排与调度:视频生成 视频生成计算成本构成分析 潜空间压缩与分辨率优化 核心机制可视化 AI系统编排与调度整体架构与关键组件
编排架构设计
回到那间忙碌的厨房。主厨第一步要做的,不是冲上去炒菜,而是把订单一张张贴在白板上,用线连起来:“黄瓜切丝 → 调酱汁 → 装盘”,“炖牛腩 → 下锅焖 → 收汁”。他发现有些菜必须先备料再烹饪,有些菜可以并行做,有些菜得等前一桌客人吃完才能上。把这些依赖关系理清楚,就是“编排”的雏形。
在 AI 系统里,编排(Orchestration)就是那个“主厨的大脑”。它负责把一个大任务拆解成若干子任务,确定它们之间的依赖关系和执行顺序。而调度(Scheduling)则像是“派活的大堂经理”——在明确了“要做什么”之后,他去协调每个厨师、每口灶台、每份食材,决定“由谁在何时何地去做”。两者分层协作,井然有序。
技术落地时,这套架构被切分为两个平面:控制平面(Control Plane) 与 数据平面(Data Plane)。控制平面是主厨的工作台,负责 DAG(有向无环图)建模、任务解析、依赖关系管理——它只做“决策”;数据平面是灶台和餐盘,负责实际执行模型推理、数据搬运——它只做“执行”。中心化的编排器(Orchestrator)运行在控制平面,维护整张 DAG,并不断把“就绪任务”交给调度器去派发。
主厨理清了所有菜品的先后顺序,就等于完成了编排的核心工作。下一节,他要处理新的麻烦:怎么记录每道菜做到哪一步了?
- 节点集合 \mathcal{V}图中的每个圆圈代表一个子任务(比如一次推理、一段数据处理):
- 边集合 \mathcal{E}每一条箭头代表两个任务之间的依赖关系,箭头方向就是执行的先后顺序:
- 有向无环约束图中不允许存在循环路径,否则任务会互相等待,永远无法结束:
任务状态管理
主厨在白板上给每道菜画了一个五格进度条:⬜待执行 → 🟦备料中 → 🟩烹饪中 → 🟨已上桌 → 🟥做砸了。每个订单从进厨房的那一刻起,就在这五个格子之间流转。任何时刻,主厨扫一眼白板,就知道全厨房的实时进度。这个“白板”,就是 AI 系统的任务状态管理。
在调度系统里,每个任务都有一个生命周期,典型的状态机包含五态:待执行(Pending)、就绪(Ready)、运行中(Running)、已完成(Succeeded) 和 失败(Failed)。任务在状态机中流转,就像菜品在白板的格子间移动。状态机的设计至关重要——它规定了每个状态下系统可以做什么操作,以及哪些事件能触发状态迁移。
状态流转不是凭空发生的。当 DAG 中某个任务的前置依赖全部完成后,它就从“待执行”变成“就绪”,进入调度队列等待派发;调度器选中它后,它变成“运行中”;执行完毕,它变成“已完成”;如果执行过程中报错抛出异常,它变成“失败”——等待重试或人工介入。这个过程中,状态信息必须持久化,否则编排器一重启,整个白板就擦没了。
白板上的五格进度条让主厨对厨房了如指掌。但新的问题来了:当十几张订单同时进入备料阶段,到底先做哪一道?
- Pending → Ready当任务的所有前置节点全部完成后,任务进入就绪队列,等待调度器挑选:
- Ready → Running调度器从队列中取出该任务,绑定资源并下发执行:
- Running → Succeeded任务正常执行结束,释放资源:
调度策略算法
午餐高峰,后厨的订单像雪片一样飞来。主厨面临一个艰难的选择:先做外卖订单?还是先做堂食大桌?如果只按先来后到,可能会让所有顾客饿肚子。如果只做小菜,大菜永远上不了桌。调度策略,就是主厨在这堆订单中寻找“最优解”的智慧。
传统调度里有两个经典策略:先来先服务(FCFS,First-Come First-Served) 和 短任务优先(SJF,Shortest Job First)。FCFS 像排队买奶茶——公平但低效,一个长任务可能堵住后面所有短任务;SJF 像“先做小凉菜再做硬菜”——平均等待时间最短,但可能出现“长任务永远被饿死”的现象。现代 AI 调度器很少单用某一策略,而是组合多种策略取长补短。
在实际系统中,优先级调度(Priority Scheduling) 最为常见:每个任务带一个优先级标签,高优任务(比如线上推理)永远排在离线批处理任务前面。而公平共享调度(Fair Scheduling) 则像一个民主食堂——每个租户按权重分得 CPU 时间片,谁也别想独占。此外还有 数据本地性亲和调度(Data Locality):尽量把任务调度到数据所在的节点上,像“让做川菜的厨师在川菜调料柜旁边工作”,避免数据跨节点搬运的开销。调度器最终通过一个资源分配函数决策:
主厨的“打分排菜法”让后厨效率大增。但调度只是“派活”,活派下去之后,食材和灶台够不够用?这就引出了资源分配问题。
- 优先级得分p_i 是任务的优先级标签,值越大代表越紧急,权重 w_p 控制它影响的力度:
- 数据本地性得分d_{i,j} 表示任务 t_i 的数据在节点 j 上的命中程度,数据在本地则得分高:
- 等待补偿w_i 表示任务已经等待的轮次,等待越久加分越多,防止低优任务被饿死:
资源分配机制
主厨有了排菜顺序,却碰上了新难题:灶台只有六个,炖锅只有三口,最好的那个双耳炒锅还有一半时间被凉菜占用。这时光靠“先来后到”排菜是不够的——得有人专门管理“哪个菜用哪口锅、用多久、能不能临时拼锅”。这就是资源分配。
AI 系统的资源分配远比厨房复杂。首先,调度器要为每个任务分配 CPU 与内存配额(像规定每道菜最多占几个灶眼、几份酱料);其次是 GPU 资源切分与隔离——一张 A100 显卡可以通过显存切片(MIG)同时服务多个小任务,就像一块大案板用隔板分成几个小操作台,彼此互不干扰。
现代集群还依赖弹性伸缩(Auto-scaling):流量高峰时自动加副本,低峰时自动缩容,像餐厅根据用餐人数调整开台数量。而 超卖(Oversubscription)与回填(Backfill) 机制则更精妙——调度器先把资源全部承诺出去,发现有空闲小窗口时,插入一些短小的批处理任务来“填缝”,像主厨在两道大菜之间的空闲灶眼上顺手炸个春卷。配额管理用如下约束建模:
资源分配这条“红线”守住了,厨房的灶台不至于过载。然而百密一疏,万一哪个灶台突然熄火,怎么办?
- 任务集合\mathcal{S}_j 表示被调度到节点 j 上的所有任务的集合:
- CPU 约束该节点上所有任务的 CPU 需求之和不能超过节点 CPU 容量:
- 内存约束同理,内存需求之和不能超过节点的内存容量:
故障处理恢复
晚上八点,后厨最忙的时刻——三口灶台中的一口突然熄火了。主厨没有慌,因为预案早已写好:他立刻把该灶台上的菜转移到备用灶台,同时让帮厨重新备一份还有八成熟的料。整个过程只用了两分钟,顾客没有任何感知。这便是 AI 系统故障处理的核心逻辑。
AI 调度系统里,故障主要靠心跳检测(Heartbeat)与健康检查(Health Check) 来发现:每个节点每隔几秒向编排器发送“我还活着”的信号,一旦超过 N 秒未收到心跳,编排器就判定该节点异常,将其上的任务标记为“调度失败,待迁移”。
故障发现只是第一步,恢复才是关键。系统会执行任务自动重试(Retry)——把失败的子任务重新放回队列,调度到其他健康节点上重新执行。对于训练类长任务,检查点机制(Checkpoint) 至关重要:系统每隔一段时间把模型权重和优化器状态保存到持久化存储中,任务挂掉后不用从头训练,只需从最近的检查点“断点续训”,像做菜做到八成熟发现盐放多了,不必从切菜重来,只要把酱汁的部分重新调一下。对于无法恢复的任务,故障转移(Failover)与熔断降级(Circuit Breaking) 会启动——把流量切换到备用服务,或者暂时返回降级结果,保证核心链路不中断。重试策略通常如此建模:
厨房的应急预案让这个夜晚有惊无险。但主厨清醒地意识到:与其每次出问题再救火,不如提前监控、提前优化。晚上打烊后,他开始翻看一天的运营数据。
- 可重试次数上限R_{\max} 是管理员配置的全局最大重试次数,防止无限重试:
- 剩余时间估算T_deadline 是任务截止时间,T_elapsed 是已经运行的时间,两者相减得到剩余可用时间:
- 平均运行时长T_avg run 是任务单轮平均运行时长,用剩余时间除以它,得到“还能跑几轮”:
性能监控优化
夜深了,主厨没有急着回家。他坐在收银台前,翻开一天的营业记录:哪个菜品出餐时间最长?哪个时段订单积压最严重?哪口灶台的使用率最低?他意识到,只有量化,才能改进。这就是 AI 系统的可观测性(Observability) 与性能优化。
AI 系统监控的关键是抓住黄金指标(Golden Signals):延迟(Latency)、吞吐(Throughput)、错误率(Error Rate) 和 资源饱和度(Saturation)。延迟是“从点菜到上菜要多久”,吞吐是“每分钟能出多少道菜”,错误率是“做砸了几道菜”,饱和度是“灶台有多忙”。这四项指标结合,基本能刻画整个系统的健康度。
监控链路从下到上分为三层:指标采集(Metrics Collection) → 聚合存储(Aggregation & Storage) → 告警可视化(Alerting & Visualization)。系统通过拉取(Pull)或推送(Push)方式采集指标,存入时序数据库(如 Prometheus),再通过 Grafana 等工具展示。当指标超过阈值(比如延迟超过 200ms),告警器会自动触发扩缩容动作,形成“监控 → 分析 → 决策 → 执行 → 再监控”的自动优化闭环。性能优化中一个常用公式是 Little's Law,它描述了系统吞吐量、并发数和延迟之间的数量关系:
主厨合上营业记录,心中有了完整的优化方案:加大监控、优化菜单、调整灶台布局。第二天,厨房运转更加流畅。
- L(在途任务数)系统中此刻正在处理和执行的任务总数,相当于“厨房里正在做的菜的数量”:
- λ(到达/吞吐率)单位时间内系统完成的任务数,相当于“每分钟上菜的数量”:
- W(平均延迟)一个任务从进系统到出系统的平均时间,相当于“每道菜从下单到上桌的分钟数”:
AI 应用场景
场景一:大模型推理平台的在线路由与容灾
阶段:模型服务部署与运维
解决的问题:大模型推理服务(LLM)面临流量高峰时,单节点 GPU 显存不足、单点故障导致服务中断。传统做法是“静态分配”——每个副本固定占用一张卡,高峰时请求排队超时,故障时用户直接收到 502 错误。
具体案例:某公司部署了 Llama-3-70B 的推理服务,平时 QPS 约 50,抢购活动时骤增至 500。他们在推理网关与 GPU 节点之间加了一层“调度编排层”:编排器预先搭建 DAG——先做 tokenizer 预处理,再查 KV cache,最后送往推理实例。调度器实时监控每个 GPU 副本的延迟与显存水位,高优请求走低延迟副本,普通请求走批处理链路;某副本响应变慢超过阈值时,健康检查自动将其摘除,请求切换至健康副本,整个过程对用户无感。
场景二:分布式训练的资源调度与断点续训
阶段:模型训练生命周期管理
解决的问题:大模型分布式训练(如千卡集群预训练)周期长达数周,任何一个节点故障都可能导致整个训练任务中断。传统方式下,任务失败后从零开始重跑,浪费大量算力成本。
具体案例:一家 AI 公司在 512 张 A100 上训练 13B 参数的 LLM。他们使用调度系统将训练任务拆分为多个子任务,并每 5 分钟自动保存一次检查点(模型权重 + 优化器状态 + 数据加载偏移量)。某次训练第 12 天时,一个节点的 GPU 发生故障,编排器通过心跳超时感知异常,将失败子任务自动调度到备用节点,从最近检查点恢复训练,仅损失了 4 分钟的算力,训练继续。同时,调度器利用回填策略,将夜间空闲算力分配给超长批处理任务,GPU 平均利用率从 45% 提升到 82%。
Python 演示
# -*- coding: utf-8 -*-"""Simplified AI task orchestration and scheduling simulator."""import heapqfrom dataclasses import dataclass, fieldfrom typing import Dict, List, Optional, Set @dataclass(order=True)class Task: """Task with priority scheduling.""" priority: int task_id: str = field(compare=False) cpu_req: float = field(compare=False) mem_req: float = field(compare=False) deps: Set[str] = field(default_factory=set, compare=False) state: str = field(default="PENDING", compare=False) class Orchestrator: """Orchestrator that manages DAG dependencies and dispatch.""" def __init__(self, cpu_cap: float, mem_cap: float) -> None: self.tasks: Dict[str, Task] = {} self.cpu_cap = cpu_cap self.mem_cap = mem_cap self.cpu_used = 0.0 self.mem_used = 0.0 self.ready_queue: List[Task] = [] self.running: Dict[str, Task] = {} def add_task(self, task: Task) -> None: self.tasks[task.task_id] = task def _all_deps_done(self, task: Task) -> bool: return all(dep in self.tasks and self.tasks[dep].state == "SUCCEEDED" for dep in task.deps) def update_state(self) -> None: for task in self.tasks.values(): if task.state == "PENDING" and self._all_deps_done(task): task.state = "READY" heapq.heappush(self.ready_queue, task) def schedule(self) -> List[str]: """Schedule ready tasks to running within resource caps.""" self.update_state() dispatched: List[str] = [] while self.ready_queue: task = self.ready_queue[0] if self.cpu_used + task.cpu_req > self.cpu_cap or self.mem_used + task.mem_req > self.mem_cap: break heapq.heappop(self.ready_queue) task.state = "RUNNING" self.running[task.task_id] = task self.cpu_used += task.cpu_req self.mem_used += task.mem_req dispatched.append(task.task_id) return dispatched def complete(self, task_id: str) -> None: task = self.running.pop(task_id) task.state = "SUCCEEDED" self.cpu_used -= task.cpu_req self.mem_used -= task.mem_req def main() -> None: orch = Orchestrator(cpu_cap=4.0, mem_cap=8.0) orch.add_task(Task(priority=3, task_id="a", cpu_req=1.0, mem_req=2.0)) orch.add_task(Task(priority=1, task_id="b", cpu_req=2.0, mem_req=4.0, deps={"a"})) orch.add_task(Task(priority=5, task_id="c", cpu_req=0.5, mem_req=1.0)) orch.add_task(Task(priority=2, task_id="d", cpu_req=1.5, mem_req=3.0, deps={"b"})) step = 0 while orch.tasks and any(t.state != "SUCCEEDED" for t in orch.tasks.values()): dispatched = orch.schedule() if dispatched: print(f"step {step}: dispatched {dispatched}") # pretend the first dispatched task finishes this step done_copy = list(orch.running.keys()) if done_copy: orch.complete(done_copy[0]) step += 1 if step > 30: print("deadlock or timeout, check DAG dependencies!") break print("final states:", {tid: t.state for tid, t in orch.tasks.items()}) if __name__ == "__main__": main()
heapq.heappush(self.ready_queue, task)按优先级把就绪任务推入最小堆,优先级数值最小的先被调度(数值越小优先级越高)task.state == "PENDING" and self._all_deps_done(task)只有所有前置依赖都成功,任务才会从 PENDING 变成 READY,这就是“状态机流转”的代码化self.cpu_used + task.cpu_req > self.cpu_cap调度器检查资源配额,CPU 用量超过节点上限就停止派发,这就是“资源红线”约束orch.add_task(Task(priority=5, ...))高优先级任务(数值大)在堆中排前面,模拟“高优任务先调度”的策略if done_copy: orch.complete(done_copy[0])模拟任务执行完成并释放资源,让调度循环能持续推进
常见误区
误区我一直以为编排和调度是同一个概念,都是“把任务派给机器跑”。我在设计系统时只做了一个“调度器”,结果任务依赖一复杂就乱套。
正解编排和调度是两件不同的事。编排(Orchestration)关注“做什么、按什么顺序做”——它负责解析 DAG、管理任务依赖和状态流转,回答的是“流程”问题;调度(Scheduling)关注“谁来做、在哪做”——它负责分配资源和决定执行时机,回答的是“资源”问题。类比厨房里,主厨负责编排(决定先备菜还是先炒菜),大堂经理负责调度(决定把菜派给哪位厨师、用哪口锅)。编排器是“大脑”,调度器是“双手”。
为何常见:很多编排框架(如 Airflow、Kubeflow)内部已经封装了调度逻辑,用起来像是一个东西,导致大家误以为两者是同一概念。等到系统规模变大、需要精细控制资源时,才发现两者需要分开设计。
误区任务状态不就是几个枚举值吗?放在内存里读写快,重启后重新拉起来再跑一遍不就行了?为什么要花功夫做持久化,还引入一整套状态机框架?
正解任务状态必须持久化到可靠的存储中(如数据库或对象存储),理由有两点。第一,编排器节点可能崩溃,如果状态只在内存里,崩溃后整个系统的“白板”就擦没了,所有任务从哪一步开始完全无法追踪。第二,状态持久化是断点续训的基础——只有知道每个任务跑到哪个检查点了,才能在故障后精确恢复。真实系统中,任务状态存储本身就是高可用的,还会记录状态迁移的历史时间戳,供后续审计和排障。
为何常见:小脚本和单机任务确实不依赖持久化状态,任务失败了重跑一遍代价不大。但到了分布式训练和千卡集群场景,重跑代价极高,人们才意识到状态持久化有多么重要。
误区既然高优任务那么重要,那调度器就应该把所有资源优先给高优任务,等它们跑完了再跑低优任务。这样既能保证高优任务的延迟,又简单粗暴。
正解如果无条件把所有资源都给高优任务,低优任务会被活活“饿死”——永远得不到执行机会,这在基建成本上是巨大浪费(GPU 空转),在业务上也会导致离线分析、批量训练等任务停滞。真实系统使用的往往是“加权公平调度”:高优任务获得更大的权重,但调度器仍然会保证低优任务获得一定的资源份额。同时还会使用等待补偿机制(等待越久优先级动态提升),防止任务饿死。
为何常见:“资源有限时优先保障重要任务”这个直觉是对的,但忽略了一个重要事实:调度系统面对的是长期、动态、多租户的负载,而非单次决策。长期看,任何极端的静态策略都会导致某种类型的任务持续饥饿。
误区任务挂掉之后重新启动一遍就好了,为什么要这么麻烦搞检查点?检查点不是还要额外消耗存储和时间吗?直接重启多省事。
正解对于训练一个 13B 参数的 LLM,单个 epoch 可能就要跑几小时。如果任务跑了 12 天才挂掉,从头重跑意味着损失 12 天的卡时成本——按千卡集群算,这是数百万美元级别的浪费。检查点机制(每 5-15 分钟存一次模型权重)让任务能从最近的断点恢复,损失的时间以分钟计算。而且现代检查点会采用异步保存策略,保存过程几乎不阻塞训练主流程,额外开销可控制在 3% 以内。这是典型的“花小钱省大钱”。
为何常见:在小模型、单机训练时代,训练一个模型只要几十分钟,挂了重跑确实损失不大。这种“重跑成本低”的旧经验被错误推广到了大模型训练场景。
误区为了掌握系统运行状态,我上了几十个监控指标:CPU、内存、磁盘 IO、网络流量、GPU 利用率、显存占用、任务队列长度……每个指标都加了图表,Dashboard 有十几个页面,但真正出问题时我还是找不到原因。
正解监控的价值不在于指标多,而在于“能否快速定位问题”。业界推荐的黄金四指标(延迟、吞吐、错误率、饱和度)之所以经典,是因为它们从用户视角和资源视角两个维度完整刻画了系统状态。指标太多反而造成“信息噪声”:每张图表都在报警,但不知道哪张图才是根因。正确做法是先建立从“用户可见的黄金指标”到“各层资源指标”的下钻路径:比如延迟升高了,先看是吞吐问题还是饱和度问题,再下钻到具体节点和具体任务,定位效率高得多。
为何常见:人类天生有“收集更多信息以便更好决策”的倾向。但在高维监控数据中做决策,人类的注意力反而会被淹没——真正专业的做法是精心选择少数几个高信号指标,并建立它们之间的因果关系链。
面试题
Q1. 在 AI 系统中,编排(Orchestration)和调度(Scheduling)之间的关系是?
- A.完全相同,两个词描述的是同一种行为
- B.编排负责任务流程与依赖,调度负责资源分配与执行← 正确答案
- C.调度负责定义任务顺序,编排负责选机器执行
- D.两者无关,一个管训练一个管推理
答案:B。编排和调度是分层协作的关系。编排是“主厨的大局观”——负责拆解任务、定义 DAG 依赖和执行顺序;调度是“大堂经理的派活”——负责把任务绑定到具体节点和资源上执行。B 准确描述了这两者的分工。A 混淆概念,C 说反了,D 更是完全错误。
Q2. 在任务状态机中,一个任务从“待执行”变为“就绪”需要满足什么条件?
- A.分配到了充足的 GPU 资源
- B.所有前置依赖任务全部成功完成← 正确答案
- C.收到了用户的启动指令
- D.通过了安全审核和权限校验
答案:B。状态机中,PENDING(待执行)→ READY(就绪)的流转条件是“所有前置依赖均已满足”——这正是 DAG 依赖编排的核心。类比厨房里,一道菜要“备料完成”才能进入“待烹饪”状态。A 是调度阶段的事,C 和 D 属于业务流程,都不是状态机流转的触发条件。
Q3. 调度策略中,“短任务优先(SJF)”可能带来什么问题?
- A.长任务平均等待时间大幅上升,甚至被饿死← 正确答案
- B.短任务永远无法获得执行机会
- C.系统资源利用率下降明显
- D.任务之间的优先级完全失效
答案:A。SJF 总是优先调度执行时间最短的任务,好处是平均等待时间最短,但代价是长任务可能长时间得不到调度,在极端情况下甚至被“饿死”。A 准确描述了这个缺陷。B 说反了,短任务不会无法执行,饿的是长任务;C 和 D 不是 SJF 的典型问题。
Q4. 某节点有 8 个 CPU 核,已分配任务占用 6 核,此时一个任务需要 3 核 CPU,调度器会怎么做?
- A.直接调度该任务到本节点
- B.拒绝调度,等待其他节点释放资源← 正确答案
- C.强制暂停已运行任务释放资源
- D.放行该任务但允许 CPU 超卖共享
答案:B。调度器必须遵守资源配额红线——节点已用 6 核,剩余 2 核,无法满足 3 核需求,所以拒绝调度到本节点,等待资源释放或其他节点。B 是正确行为。A 违反资源约束会导致节点过载;C 是破坏性的动作,调度器不会主动暂停运行中任务;D 涉及到超卖策略,默认情况下不会这么做。
Q5. 检查点(Checkpoint)机制在大模型训练中的核心目标是什么?
- A.加快单次前向传播的推理速度
- B.防止模型过拟合训练数据
- C.故障后从最近断点恢复,减少算力损失← 正确答案
- D.减少 GPU 显存的占用和碎片
答案:C。检查点机制是定期把模型权重和优化器状态保存到持久化存储,当任务故障时可从最近断点恢复,避免从头训练。类比做菜做到八成熟时保存进度,而不是每次失败都从切菜重来。C 是核心目标。A 是推理优化,B 是正则化,D 是显存管理,均不是检查点的目的。
Q6. 根据 Little's Law,当系统并发任务数不变但吞吐量下降时,最可能发生什么?
- A.平均延迟会随之上升← 正确答案
- B.平均延迟会随之下降
- C.任务错误率会明显提高
- D.系统资源利用率会下降
答案:A。Little's Law 公式 L = λ × W 告诉我们,并发数 L 基本恒定且吞吐量 λ 下降时,平均延迟 W = L/λ 必然上升。类比厨房:同时出的菜数量不变,但出菜速度变慢,唯一的解释是每道菜在厨房里停留的时间变长了——这通常意味着系统存在瓶颈。A 是直接结论,B 方向相反,C 和 D 不是 Little's Law 的直接推论。
Q7. 以下哪些属于 AI 系统监控的“黄金指标”?
- A.延迟(Latency)← 正确答案
- B.吞吐(Throughput)← 正确答案
- C.GPU 温度(Temperature)
- D.错误率(Error Rate)← 正确答案
答案:ABD。黄金四指标包括延迟、吞吐、错误率和饱和度。它们从用户感知(延迟、错误率)与资源能力(吞吐、饱和度)两个维度刻画系统健康。GPU 温度属于硬件层指标,虽然值得监控但不是黄金指标体系的一部分。A、B、D 是黄金指标,C 不是。
Q8. 公平共享调度(Fair Scheduling)相比优先级调度的优势在于?
- A.防止低优先级任务被长期饿死← 正确答案
- B.保证不同租户按权重公平分享资源← 正确答案
- C.系统整体吞吐量一定高于 FCFS
- D.高优先级任务永远等待时间最短
答案:AB。公平共享调度的核心设计哲学是“谁都不能被饿死”——每个租户/任务按权重获得最小资源保证,A 和 B 是它的核心优势。C 过于绝对,吞吐量取决于综合负载类型;D 与公平调度理念矛盾,公平调度会刻意限制高优任务的资源占用上限以保证公平。
Q9. 某训练任务的截止时间为 11:00,当前已运行 1 小时,单轮平均运行 30 分钟,距离截至还有 60 分钟,管理员设定的最大重试次数为 5,该任务故障后最多还能重试几次?
- A.1 次
- B.2 次← 正确答案
- C.3 次
- D.5 次
答案:B。根据重试公式 retries = min(R_max, floor((T_deadline - T_elapsed) / T_avg_run)),剩余时间为 60 分钟,单轮平均运行 30 分钟,floor(60/30)=2 次,min(5, 2)=2 次。B 正确。A 漏算了剩余时间能跑两轮,C 和 D 超出了剩余时间允许的重试次数,会导致任务过期。
Q10. 关于“数据本地性亲和调度”,以下哪些说法正确?
- A.将任务调度到数据所在节点,减少跨节点数据传输← 正确答案
- B.当数据本地节点资源不足时,可以等待或选择远程执行← 正确答案
- C.数据本地性总是最优选择,不应考虑其他因素
- D.它是提升深度学习训练中 IO 效率的重要手段← 正确答案
答案:ABD。数据本地性调度的核心思想是“让计算靠近数据”。A 直接描述了这一原则;B 正确——当本地节点资源不足时,系统会权衡等待成本与远程传输成本,选择折中方案;D 正确,训练中大量参数读取,本地读取能显著减少 IO 瓶颈。C 过于绝对,本地性不是唯一考量,资源均衡同样重要。