想象一家融合餐厅的后厨:蒸箱、铁板、烤炉、凉菜台各有绝活,但它们隔着好几条走廊。晚市高峰,大管家阿健必须决定每道菜该去哪个工作台、以什么顺序做。如果把烤鸭端到凉菜台,菜会难吃;如果为一道拍黄瓜动用烤炉,纯属浪费。异构计算调度里的“设备亲和性”,就是给每个 AI 计算任务找最合适硬件的艺术。任务有自己的“脾气”,设备有自己的“家底”,调度器要做的,是在搬运代价、忙碌程度、出菜速度之间找到平衡。这篇内容用阿健的后厨视角,讲清设备亲和性调度优化技术方案。
掌握设备亲和性调度,是构建高效 AI 推理平台和异构资源池的核心能力,能直接决定线上服务的吞吐、延迟与成本。
目录
可视化演示
展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。
本节配有交互动效(进入视口后自动加载,约 3KB)。
系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T
亲和性概念:建立“最优匹配”的直觉
阿健刚当上融合餐厅后厨总管时,最头疼的不是菜谱,而是“这道菜到底该给哪个工作台”。蒸箱擅长软糯、铁板擅长爆炒、烤炉擅长焦香,把任务送错工作台,出菜慢而且味道差。这个直觉,正是异构计算里“设备亲和性”的核心:每个计算任务都有一个最“对味”的硬件设备。
异构计算系统里常见 CPU、GPU、NPU 等设备,它们擅长不同形状的计算。所谓设备亲和性,就是任务特征与设备能力之间的匹配程度。调度器本身不改变算子,只负责在任务到达时做“派单”决策。AI 计算图中一个算子该去哪个设备,不是拍脑袋决定,而是通过一个匹配函数来评估,输出一个亲和度分数。
阿健的后厨里,菜和灶台之间仿佛有一条条看不见的“契合线”,调度器就是沿着这些线把混乱变成秩序。先看懂菜,再摸清灶台,才能真正用好这条契合线。
- 亲和分下标
S_{t,d}表示第t个算子放到第d个设备上的匹配得分,得分越高越合适。 - 匹配评估函数
\Phi(\cdot)是调度器内置的匹配函数,它接收任务特征和设备能力两组输入。 - 任务特征向量
\mathbf{T}_{t}描述算子自身的计算密度、访存方式、并行度等特性。 - 设备能力向量
\mathbf{D}_{d}描述设备算力、带宽、缓存、加速器特性等能力标签。
算子特征:先看清楚每道菜
阿健会记下每道菜的三个关键信息:工序复杂不复杂、取料频不频繁、能不能同时多锅炒。AI 计算图里的“算子”也是一道道菜,它是模型推理中最基本的计算单元,比如卷积、矩阵乘、查表等。调度器只有读懂算子的特征,才能知道它适合“猛火快炒”还是“小火慢炖”。
算子特征中最常用的是计算密度和内存访问模式。计算密度衡量的是“每搬运一个字节的数据,要做多少次计算”。如果计算量远大于数据量,就是计算密集型算子;如果计算量不大但数据搬来搬去,就是访存密集型算子。此外,算子类型和并行度也很重要:矩阵类算子天然可并行,适合 GPU;串行依赖强的控制逻辑则更适合 CPU 快速执行。
阿健现在拿到每一道菜,都会先在心里给它的“工序复杂度”和“取料频次”打分。这个习惯落到 AI 调度中,就是对算子的特征分析,它是后续所有亲和性判断的基础。
- 计算量
O_{\text{ops}}是算子的总浮点运算次数,表示这道菜需要多少“颠勺”动作。 - 搬运数据量
D_{\text{bytes}}是算子处理过程中需要读取和写出的字节数,相当于从仓库搬到灶台的食材总量。 - 计算密度
\Pi是两者的比值,数值高代表计算密集,适合高算力设备;数值低代表访存密集,适合高带宽设备。
设备能力:再摸清每个灶台
阿健清点过每个工作台的“家底”:火力上限、能同时放几口锅、锅铲能不能快速转身。设备能力画像就是给异构硬件做同样的盘点。CPU 像是全能的家庭灶,响应快但峰值火力有限;GPU 像是超大铁板,适合大量并行爆炒;NPU 则更像流水线专业烤炉,对特定算子做了深度定制。
设备能力画像至少包括四个维度:峰值算力、存储带宽、缓存容量和加速器特性。峰值算力决定它能做多重的计算;存储带宽决定它搬运食材的速度;缓存容量决定它能不能把常用食材放在手边;加速器特性决定它有没有特殊的“秘制酱料”,比如矩阵乘加速单元。这些维度组合起来,就形成一个设备的能力向量。
阿健把每个灶台都画成一张“能力卡”,贴在调度台前。设备能力画像越准确,他在高峰期的选择就越有底气。
- 峰值算力
P_d是设备单位时间能完成的最大计算量,相当于灶台的最大火力。 - 存储带宽
B_d是设备与内存之间的数据搬运速度,相当于传菜传送带的速度。 - 缓存容量
C_d是设备内部能暂存数据的大小,相当于后厨里的置物架空间。 - 加速器特性
Q_d表示设备是否有针对特定算子的专用能力,比如矩阵乘单元、向量指令集等。
调度匹配策略:打分、约束与动态调整
阿健不会只看“哪个灶台火力最大”就做决定,他手里有一张评分表:算力匹配、数据是否已经在附近、把菜端过去要多久,还有这道菜会不会把某个灶台占死。调度匹配策略就是把这套评分规则形式化,变成可计算的亲和度评分。
实践中常常用加权评分把多个因素合成一个分数:算力匹配度加分,数据局部性加分,跨设备搬运代价减分。除打分之外,调度器还要满足硬件约束,例如某个设备的显存不足、某个加速器不接受某种算子、某个设备不能超时占用。由于线上负载不断变化,调度器还会周期性或按事件触发动态调整,而不是只做一次静态派单。
阿健的评分表不是一成不变的:如果某个工作台排了长队,他会临时把不太“对味”的菜派到邻炉;如果食材刚好在某灶台旁边,他也会优先让这道菜留在原地做。这种灵活,就是动态调度调整的价值。
- 权重系数
\alpha、\beta、\gamma是调度器根据业务目标设置的权重,表示这几方面的重要程度。 - 算力匹配度
C_{t,d}是任务计算需求与设备算力的匹配程度,越接近越合适。 - 数据局部性
M_{t,d}是数据已经驻留在该设备或邻近存储的程度,数据越“近”分数越高。 - 传输代价
T_{t,d}是跨设备搬运数据的代价,因为它会拖慢整体速度,所以从总分中扣除。
跨设备开销:搬运成本也要算
阿健知道,把半成品从蒸箱端到铁板,要走一条不短的走廊。高峰期走廊挤满传菜员,端菜的时间甚至可能超过做菜的时间。在异构计算中,跨设备数据传输也常常成为被低估的成本,尤其是小算子,计算本身只需要几微秒,数据传输反而需要几十微秒。
跨设备开销包括几个部分:数据搬运时间、同步延迟、启动开销以及带宽瓶颈。同步延迟是指两台设备要互相等待对齐的时间;启动开销是发起一次数据传输或内核执行时的固定成本;带宽瓶颈则是链路被大量数据同时争抢时出现的拥塞。这些开销意味着:强行把任务分配给“算力最强”的设备,可能因为搬运代价而得不偿失。
阿健的经验是:小小一盘凉菜端到另一个厨房根本不划算,跨设备的固定开销会吞掉所有省下来的时间。所以,真正的调度高手会把“搬运成本”写进每天的算账本里。
- 搬运数据量
D_{\text{data}}是需要跨设备传输的数据大小,相当于那筐食材的分量。 - 链路带宽
B_{\text{link}}是设备间链路的有效传输带宽,相当于走廊每分钟能通过多少人。 - 搬运时间
\frac{D_{\text{data}}}{B_{\text{link}}}是理论上把数据全部传完需要的时间。 - 固定延迟
L_{\text{latency}}是每次传输都要付出的一次性开销,包括同步握手和启动时间。
调度优化目标:吞吐、延迟、利用率与能耗
阿健的目标不是让某个工作台忙到冒烟,而是整晚多翻台、客人少抱怨、后厨不超负荷、电费别太夸张。异构调度的优化目标也是多维的:系统吞吐量要高,单位时间能处理更多请求;延迟要满足服务质量要求,不能出现长时间等待;设备利用率要合理,既不要空转也不要排队过深;能耗还要受控,尤其在边缘和数据中心大规模部署时。
这些目标常常互相牵制。比如,为了利用率把任务都塞给 GPU,可能导致计算卡排队,平均延迟上升;为了省电把任务都放 CPU,又可能让吞吐量大幅下降。因此,调度器通常用一个带权重的综合目标函数来做权衡,把吞吐、延迟、能耗统一成可比较的分数。
阿健后来明白:后厨管理不是“把某道菜做到极致”,而是整晚的系统性平衡。设备亲和性调度也一样,它服务的不是一个算子,而是一整套业务目标。
- 吞吐收益
\lambda_T \times \text{Throughput}是系统吞吐量带来的收益,吞吐越高,综合分越高。 - 延迟代价
\lambda_L \times \text{Latency}是平均延迟带来的惩罚,延迟越高,综合分越低。 - 能耗代价
\lambda_E \times \text{Energy}是能耗带来的惩罚,提醒调度器不要只为速度牺牲能效。 - 加权系数\lambda_T、\lambda_L、\lambda_E 是面向业务场景设定的权重,例如在线服务会更看重延迟。
AI 推理部署:把后厨经验做实
阿健最终把自己的排菜经验写成了一套软件,餐厅从此能在高峰期自动应对突发客流。设备亲和性调度落到 AI 推理服务中,正是这样一套“经验软件化”的过程。推理服务面对的不是一批固定的算子,而是大量不断到达的请求,因此调度器必须支持多路复用、动态扩容、负载均衡和端到端优化。
多路复用意味着同一个推理服务里不同模型、不同算子可以共享异构设备;动态扩容指的是在流量上升时把更多算子调度到新增设备上;负载均衡则避免某个设备过热、其他设备空转。端到端优化要求调度器不仅看单个算子,还要看一次请求从数据进入、算子执行、设备搬运到最终返回的总延迟。
阿健的后厨软件上线后,菜单、灶台、走廊和排队被统一编排。AI 推理平台的设备亲和性调度也是如此:把每道算子的“脾气”和每个设备的“家底”抽象成数据,再通过调度器在正确的时间放到正确的位置。
- 计算时间
L_{\text{compute}}是算子在设备上真正执行计算消耗的时间。 - 传输时间
L_{\text{transfer}}是数据在设备之间搬运、同步所花费的时间。 - 排队时间
L_{\text{queue}}是任务到达设备后等待被调度执行的时间,相当于客人的等位时间。 - 端到端延迟
L_{\text{total}}是请求从进入到返回的总延迟,也是推理服务最关心的服务质量指标之一。
AI 应用场景
AI 推理服务中的多模型共置
阶段:模型推理部署与算子调度
解决的问题:传统做法经常把整个模型固定到一张 GPU 上,导致一个平台同时服务多个模型时出现算力浪费和频繁跨卡拷贝。利用设备亲和性,调度器可以拆分模型内部算子,让访存密集部分放到大带宽设备,计算密集部分放到高算力设备,整体降低资源占用。
具体案例:推荐系统里既有 embedding 查表,也有深度神经网络。查表算子数据量大但计算简单,放在 CPU 或大缓存设备;神经网络计算密集,放到 GPU/NPU。调度器按亲和度打分后自动分配,数据预取也同步完成,线上 p99 延迟明显下降。
边缘-云协同推理
阶段:边缘推理与云侧卸载
解决的问题:边缘设备算力有限、电池宝贵,如果所有任务都上传云端,会引入巨大网络延迟。利用设备亲和性调度,系统先给任务做本地与云端的亲和度评估:本地能做的算子留在边缘,只有重计算算子才卸载到云端。
具体案例:智能安防摄像头先在本地的 NPU 上完成目标检测,再把检测到的目标特征图传到云端做大模型语义理解。相比直接传视频流,跨设备数据量减少 90% 以上,时延也从秒级降到百毫秒级。
动态扩缩容中的亲和性决策
阶段:资源弹性调度
解决的问题:云上 GPU 集群经常出现“有卡但排队很久”的情况,传统调度只关心设备是否空闲,忽略了任务与设备之间的亲和关系。设备亲和性调度在扩容和缩容时会综合亲和分、当前负载和传输代价,避免把任务塞进“最贵但最堵”的设备。
具体案例:某云厂商的推理集群在高峰期将一部分批处理算子调度到空闲 CPU 核,牺牲少量延迟换取整体吞吐提升;当 GPU 队列变短后,再把算子动态迁移回 GPU,整个过程对用户请求透明。
Python 演示
import math # 设备能力画像:算力、带宽、缓存、加速器特性分devices = { "CPU": {"flops": 120, "bandwidth": 40, "cache": 16, "simd": 0.4}, "GPU": {"flops": 1200, "bandwidth": 600, "cache": 6, "simd": 0.9}, "NPU": {"flops": 900, "bandwidth": 300, "cache": 4, "simd": 1.0},} # 算子特征:ops=计算量,data=数据量,locality=数据局部性tasks = [ {"name": "conv_bn_relu", "ops": 1500, "data": 12, "locality": 0.6}, {"name": "embedding_lookup", "ops": 80, "data": 30, "locality": 0.9}, {"name": "matrix_mul", "ops": 2000, "data": 10, "locality": 0.5},] def compute_density(ops, data): if data <= 0: return float("inf") return ops / data def affinity_score(task, dev): density = compute_density(task["ops"], task["data"]) / 10.0 compute_fit = min(dev["flops"] / max(task["ops"], 1.0), 1.0) mem_fit = 1.0 - abs(task["locality"] - dev["bandwidth"] / 800.0) return 0.5 * compute_fit + 0.3 * mem_fit + 0.1 * dev["simd"] + 0.1 * min(density, 1.0) def pick_device(task): best_name = None best_score = -1.0 for name, dev in devices.items(): score = affinity_score(task, dev) if score > best_score: best_score = score best_name = name return best_name, best_score def schedule(task_list): plan = [] for task in task_list: dev, score = pick_device(task) plan.append((task["name"], dev, round(score, 3))) return plan def main(): plan = schedule(tasks) for name, dev, score in plan: print(name, "->", dev, "affinity=", score) if __name__ == "__main__": main()
def compute_density(ops, data):计算算子的计算密度,判断它是“力气活”还是“搬运活”。density = compute_density(task["ops"], task["data"]) / 10.0把计算密度归一化到可比较的范围内。compute_fit = min(dev["flops"] / max(task["ops"], 1.0), 1.0)表示设备算力对任务需求的满足度,最高取 1。mem_fit = 1.0 - abs(task["locality"] - dev["bandwidth"] / 800.0)衡量数据局部性和设备带宽特性能否“对上味”。return 0.5 * compute_fit + 0.3 * mem_fit + 0.1 * dev["simd"] + 0.1 * min(density, 1.0)把多个匹配维度加权成最终亲和度分数。plan.append((task["name"], dev, round(score, 3)))保存调度结果,方便统一输出和排查。
常见误区
误区GPU 算力最强,所以推理时把每个算子都放到 GPU 上一定最快,设备越贵越好。
正解设备亲和性不是选“最强”,而是选“最合适”。GPU 算力再高,如果数据要从 CPU 内存千里迢迢搬过来,或者算子本身只有一点点计算,搬运和启动开销会把收益全部抵消。就像阿健不会用烤炉去做拍黄瓜,成本高、效果差。
为何常见:深度学习训练常以 GPU 为核心,大家容易把“训练最快”的经验直接外推到所有推理算子上。
误区把每个设备都塞满任务,让它一直 100% 工作,系统吞吐就会最高。
正解过度追求利用率会带来长队列和严重排队延迟。调度目标还包括延迟 SLA、能耗和负载均衡,有时候适当让设备空一点点,反而能让整条链路更顺畅。后厨里每个灶都忙到冒烟,客人未必能更快吃到菜。
为何常见:人们习惯用“是否空闲”来判断资源好坏,忽略了排队本身也是一种成本。
误区判断任务和设配是否匹配,只需要比较设备算力大小就行。
正解真实的亲和度评分还包含内存带宽、缓存大小、数据在哪个设备、传输代价、同步延迟和能耗。算力最强的设备如果数据不在本地,搬运后反而不如算力稍弱但数据就绪的设备。
为何常见:厂商宣传喜欢强调峰值算力,久而久之会给从业者造成“算力决定一切”的错觉。
误区模型部署时把算子分好设备,之后就不用再管了。
正解线上负载、设备健康状态和请求分布都在动态变化。真正可用的调度器会做周期性评估或事件触发调整,例如设备排队变深时把任务迁移,流量突增时把算子扩容到其他设备。阿健也不会在晚市开始后就不管后厨了。
为何常见:把部署和调度理解成“安装时设置一次”的静态动作,忽略了分布式系统的不稳定性和波动性。
面试题
Q1. 设备亲和性调度的核心目标是什么?
- A.让所有任务都跑最快设备
- B.让每个计算任务匹配合适设备← 正确答案
- C.让所有设备保持相同负载
- D.让训练误差降到最低水平
答案:B。设备亲和性强调“合适”而非“最强”。A 忽略传输代价,C 属于负载均衡而不是亲和性的核心,D 是模型训练目标。让每个任务匹配最合适的设备,才是亲和性调度的本质。
Q2. 下面哪种算子最适合放到访存带宽高的大内存设备上?
- A.计算密集型的矩阵乘法
- B.访存密集型的查表运算← 正确答案
- C.同步开销大的控制算子
- D.纯标量运算的简单算子
答案:B。访存密集型算子的性能瓶颈是数据搬运速度,高带宽大内存设备能减少等待。矩阵乘法更需要高算力。控制算子和标量运算对带宽依赖较低,不是最优选择。
Q3. 计算密度的含义是什么?
- A.单位数据量对应的计算量← 正确答案
- B.单位时间消耗的能量大小
- C.每颗芯片上的核心数量
- D.缓存容量除以内存带宽
答案:A。计算密度 = 算子总计算量 / 数据搬运量,用于判断任务是计算密集还是访存密集。B 是功耗指标,C 是核心数,D 是缓存和带宽的比值,都不是计算密度。
Q4. 在亲和度评分中,跨设备传输代价通常如何处理?
- A.作为亲和度评分中的加分项
- B.作为亲和度评分中的减分项← 正确答案
- C.只在启动阶段短暂考虑
- D.只在设备空闲的时候考虑
答案:B。跨设备传输会带来延迟和资源占用,理应降低亲和度总分。如果把它当成加分项,调度器会把所有任务都推向远端设备,导致系统整体性能下降。
Q5. 下列哪些属于设备能力画像中的关键维度?
- A.峰值算力← 正确答案
- B.内存带宽← 正确答案
- C.缓存容量← 正确答案
- D.任务排队长度
答案:ABC。峰值算力、内存带宽、缓存容量都属于设备本身的静态能力。任务排队长度是运行时的负载状态,不属于能力画像维度,但可用于动态调度调整。
Q6. 当一个小算子需要频繁访问远端设备的数据时,调度器最可能把它放在哪里?
- A.算力最强的计算设备
- B.数据所在的本机设备← 正确答案
- C.空闲但无数据的设备
- D.能耗最低的边缘设备
答案:B。数据局部性优先。如果数据已经在某个设备上,把算子放到相同设备可以避免传输开销。算力最强但没数据,可能面临高昂搬运成本;空闲无数据和能耗低都不是首要考虑。
Q7. 下列哪些属于异构调度要优化的目标?
- A.提升系统吞吐量← 正确答案
- B.满足延迟服务质量← 正确答案
- C.提高设备利用率← 正确答案
- D.降低模型参数量
答案:ABC。吞吐、延迟 SLA、利用率都是调度器的核心优化目标。模型参数量属于模型结构设计,不是调度器的职责范围,因此 D 不属于调度优化目标。
Q8. AI 推理服务中,embedding 查表类算子通常更适合放在哪个设备?
- A.GPU 专用矩阵单元
- B.大缓存高带宽的内存设备← 正确答案
- C.低延迟专用推理芯片
- D.远端的大规模计算集群
答案:B。embedding 查表计算量小但数据访问量大,瓶颈在访存而非计算。大缓存高带宽设备能显著减少访存延迟。GPU 矩阵单元强在大量乘加计算,对查表类算子优势不大。
Q9. 为什么“把所有任务都放到 GPU”往往不是最优方案?
- A.跨设备数据搬运会产生额外开销← 正确答案
- B.GPU 算力无法弥补访存瓶颈← 正确答案
- C.会造成单设备队列排队过深← 正确答案
- D.会显著降低模型推理精度
答案:ABC。全放 GPU 会增加搬运成本,访存密集型算子也发挥不出 GPU 优势,还容易让 GPU 排队严重。推理精度与部署设备没有必然关系,因此 D 错误。
Q10. 在动态异构调度中,调度器做实时决策时最应该依赖哪类信息?
- A.设备当前负载与链路状态← 正确答案
- B.芯片厂商的官方宣传手册
- C.训练数据集的标签分布
- D.模型保存时的参数量
答案:A。动态调度需要在当下做出决策,设备当前负载、排队长度和链路带宽是反映实际状况的实时信息。厂商手册描述的是理论峰值,训练标签和参数量与运行时资源状态无关。