老陈的深夜食堂,后厨里站着三位性格迥异的师傅:一位什么菜都会做的全能主厨,一组擅长把大批食材切成均匀土豆丝的流水线切配班,以及一台只会炖招牌汤的 AI 炖汤机。三位各有绝活,也各有短板。晚上大厅同时涌入十几桌客人,老陈必须想清楚:哪道菜交给谁做、灶台不够时先出哪桌、哪台设备正闲着。这就是边缘设备上的异构计算调度——CPU 像全能主厨,GPU 像切配流水线,NPU 像专用炖汤机,而 OpenCL、Vulkan 是两套统一的“锅铲接口”,调度框架则是老陈手里的派单看板。本文就用这个后厨故事,带你理解动态分配任务、后端选型、算子映射、多路并发和性能调优。

岗位关联

对 AI 从业者而言,本章能帮你把“模型能跑”变成“模型跑得稳、跑得快、跑得省电”,是边缘物联项目落地时区分业余与专业的关键能力。

目录
  1. 可视化演示
  2. 异构设备画像
  3. 计算后端选型
  4. 算子映射策略
  5. 多路并发调度
  6. 调度框架设计
  7. 性能调优实践
  8. 边缘部署保障
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

演示算子级设备映射将计算图算子分配至CPU/GPU/NPU、OpenCL/Vulkan计算后端选择与算子融合、多模型并发推理调度时间线与资源利用率三大模式交互动画。

本节配有交互动效(进入视口后自动加载,约 3KB)。

算子级设备映射调度 计算图算子按特性分配至CPU/GPU/NPU最优设备 Conv算子映射NPU矩阵运算Softmax映射CPU device(op) = argmin_i {latency(op, dev_i)} 算子级映射最大化异构硬件综合利用率

异构设备画像

老陈的后厨里,主厨、切配班和炖汤机各有各的脾气。全能主厨什么菜都会做,但只有一双手,同时应付三桌就开始手忙脚乱;切配班几十只手一起上,切土豆丝、胡萝卜丁极快,但遇到需要临时改刀的花色菜就笨拙;AI 炖汤机炖招牌汤又快又稳,可你要它炒青菜,它只会把青菜也炖成汤。边缘设备里的 CPU、GPU、NPU 正是这样三种能力完全不同的计算单元,把它们放在同一块板子上,就构成了“异构设备”。

CPU 的优势是通用,能处理动态分支、异常、系统调用;GPU 的优势是大规模并行,适合卷积、矩阵乘这类“同一套动作做很多份”的运算;NPU 则更专一,内部堆满了乘加阵列,做神经网络里的密集算子时能效比极高。可异构也带来麻烦:老陈不能凭“谁名气大”就派单,还得看这道菜到底需要什么工艺。

衡量异构收益时,最经典的是 Amdahl 定律给出的视角:

老陈看懂了这张公式:与其把十道菜都塞给炖汤机,不如先想清楚哪些步骤只能主厨做。边缘设备里的调度,第一步就是摸清这三类师傅的家底。

Ttotal=Tserial+TparallelN\displaystyle T_{\rm total} = T_{\rm serial} + \frac{T_{\rm parallel}}{N}
通俗说明:总耗时等于“只能一个人做的部分”加上“能分给很多人做的部分”。并行度再高,串行部分还在那里等着。
  1. 串行部分 \(T_{\rm serial}\)必须由某一个通用单元完成的环节,例如动态形状处理和跨设备的控制逻辑,无法被并行摊薄。
    TserialT_{\rm serial}
  2. 并行部分 \(T_{\rm parallel}\)可以切成许多小片交给多核、多线程或并行单元同时做的计算,例如多路卷积。
    TparallelT_{\rm parallel}
  3. 除以并行度 \(N\)当有 \(N\) 个并行单元同时工作时,这部分理论耗时被压缩为原来的 \(1/N\)。
    TparallelN\frac{T_{\rm parallel}}{N}
  4. 相加得到总耗时串行和并行两部分无法完全重叠,所以总时间等于两者之和。
    Ttotal=Tserial+TparallelNT_{\rm total} = T_{\rm serial} + \frac{T_{\rm parallel}}{N}

计算后端选型

老陈想给三位师傅配一套统一的“锅铲接口”,而不是每个师傅都用自己的私房工具。OpenCL 和 Vulkan 就是这样的计算后端,它们都不是硬件,而是“厨房操作手册”。OpenCL 是历史更悠久的通用手册,菜谱写一次,CPU、GPU、甚至部分 NPU 都能照着做;Vulkan 则更像一本要求极高的精细手册,它让开发者明确控制每一步“开火、关火、端锅”,减少了很多隐形等待。

选哪本手册,本质是在比较四段额外时间:准备数据、拷贝数据、启动内核、执行内核。OpenCL 的驱动更成熟,可移植性好;Vulkan 的启动开销和同步控制更细,适合延迟敏感的实时任务。边缘设备上驱动支持千差万别,因此要先做“能力探测”,再决定默认后端和降级顺序。

一次完整的后端调用,总耗时可以写为:

老陈最终没有迷信“最贵的手册”,而是给后厨装了一本主手册加一套应急手册:默认用 OpenCL 保证兼容,遇到毫秒级实时任务就切到 Vulkan 精细化控火。

Tbackend=Tprepare+Tcopy+Tlaunch+Tkernel\displaystyle T_{\rm backend} = T_{\rm prepare} + T_{\rm copy} + T_{\rm launch} + T_{\rm kernel}
通俗说明:后端总耗时不只包含真正的计算,还包括准备工作、数据搬运和启动命令的开销。
  1. 准备阶段 \(T_{\rm prepare}\)创建缓冲区、命令队列和内核对象,相当于把食材和锅具先摆到台面上。
    TprepareT_{\rm prepare}
  2. 拷贝阶段 \(T_{\rm copy}\)把输入数据从主机内存搬到设备显存或专用内存,边缘设备上这一步常常比想象中慢。
    TcopyT_{\rm copy}
  3. 启动阶段 \(T_{\rm launch}\)向设备提交命令并等待其开始执行,Vulkan 的设计目标之一就是压低这里的延迟。
    TlaunchT_{\rm launch}
  4. 内核执行阶段 \(T_{\rm kernel}\)设备真正运行算子的时间,往往只占总耗时的不到一半。
    Tkernel=Tbackend−Tprepare−Tcopy−TlaunchT_{\rm kernel} = T_{\rm backend} - T_{\rm prepare} - T_{\rm copy} - T_{\rm launch}

算子映射策略

后厨里一张菜单可以被拆成“切菜、腌渍、爆炒、摆盘”等工序,每个模型推理也一样,可以拆成一个个算子:卷积、矩阵乘、激活、池化、拼接、变形等。算子映射,就是老陈决定每一道工序交给哪位师傅。切配班适合批量切丝,炖汤机适合长时间慢炖,全能主厨则适合处理临时改刀和摆盘。

要做出决定,不能只看“谁单次做得最快”,还要看工序前后的“搬运费”。数据从内存搬到 NPU 要时间,算完再从 NPU 搬回也要时间。如果一道算子本身只要 0.1 毫秒,搬运却要 1 毫秒,那这单生意根本不划算。因此设备能力矩阵里记录的,应该是“计算耗时 + 搬运耗时”的总成本。

一个算子 \(op\) 映射到设备 \(d\) 上的最优选择可以写成:

老陈把这张“成本表”贴在调度板上之后,后厨再也不出现“炖汤机为了炒一盘青菜折腾十分钟”的笑话。映射不是拍脑袋,而是算账。

d∗=arg⁡min⁡d∈{CPU,GPU,NPU}(Tcomp(op,d)+2SopBd)\displaystyle d^{*} = \arg\min_{d \in \{ \text{CPU}, \text{GPU}, \text{NPU} \}} \left( T_{\rm comp}(op, d) + \frac{2 S_{op}}{B_d} \right)
通俗说明:选设备时,不再只问“谁算得快”,而是问“算完加上来回搬运,谁总时间最短”。
  1. 候选设备集合 \(d \in \{\text{CPU}, \text{GPU}, \text{NPU}\}\)每个算子都要在这三类设备中做一次成本比较。
    d∈{CPU,GPU,NPU}d \in \{\text{CPU}, \text{GPU}, \text{NPU}\}
  2. 设备上的计算耗时 \(T_{\rm comp}(op, d)\)根据能力矩阵查到的理论执行时间,通常由历史 benchmark 统计得到。
    Tcomp(op,d)T_{\rm comp}(op, d)
  3. 数据搬运耗时 \(\frac{2S_{op}}{B_d}\)\(S_{op}\) 是算子的输入输出数据量,\(B_d\) 是设备可用带宽,系数 2 表示输入搬入和输出搬回。
    2SopBd\frac{2 S_{op}}{B_d}
  4. 取下标最小化 \(d^{*} = \arg\min\)在所有候选中找出总成本最低的那个设备。
    d∗=arg⁡min⁡d(Tcomp(op,d)+2SopBd)d^{*} = \arg\min_{d} \left( T_{\rm comp}(op, d) + \frac{2 S_{op}}{B_d} \right)

多路并发调度

晚上八点,食堂同时来了三桌客人:一桌要招牌汤,一桌要爆炒牛肉,一桌只要凉拌菜。老陈不能再像只接待一桌时那样随心所欲,他需要一条“派单规则”。边缘设备上的多模型并发推理也一样,摄像头流、语音助手、传感器告警可能同时到达,谁拥有更高优先级、谁能被延后、谁必须抢占设备,都需要调度器回答。

最常用的思路是优先级加权队列:每个模型请求带一个优先级,高优先级任务排到队列前面;同一优先级内按到达顺序服务。为了让低优先级任务也不至于饿死,调度器会按权重分配时间片或资源份额,而不是让高优先级任务永远独占总线。

设第 \(i\) 个模型请求的优先级为 \(p_i\),总共有 \(M\) 个请求,则它获得的资源份额为:

老陈还在调度板上加了一条:如果炖汤机已经被招牌汤占住,就把爆炒牛肉放到切配班,而不是让所有任务堵在同一个灶台前。多路并发调度,本质是“按优先级排队,按能力分流”。

wi=pi∑j=1Mpj,qi=wi×Q\displaystyle w_i = \frac{p_i}{\sum_{j=1}^{M} p_j}, \qquad q_i = w_i \times Q
通俗说明:把请求按优先级归一化成比例,再乘上总资源 \(Q\),就知道每个模型该分到多少时间片。
  1. 优先级 \(p_i\)每个请求的紧急程度,数值越大表示越优先,例如车辆告警比日志分析更紧急。
    pip_i
  2. 归一化权重 \(w_i\)用单个优先级除以所有请求优先级之和,得到它在全局中的相对占比。
    wi=pi∑j=1Mpjw_i = \frac{p_i}{\sum_{j=1}^{M} p_j}
  3. 资源份额 \(q_i\)用权重乘上总时间片 \(Q\),得到该请求在这一轮里可以使用设备的时长。
    qi=wi×Qq_i = w_i \times Q

调度框架设计

有了设备、后端和映射规则,还需要一个真正能落地的“后厨总管”:调度框架。老陈的调度板不是一张纸,而是一套会自己更新的系统。计算图任务化,是把模型推理拆成一个有依赖关系的任务图;状态机,是让每个任务在等待、就绪、运行、完成之间明确流转;线程池,是控制后厨里同时跑动的传菜员数量;事件循环,则是不断检查哪个设备已经空闲、哪个任务的结果已经回来。

一个成熟的边缘推理调度框架,通常会避免“做完一个再做下一个”的串行模式,而采用流水线式并行。整体吞吐量不再由所有步骤之和决定,而是由最慢的那个环节决定:

老陈终于把后厨从“凭经验喊单”升级成“看板驱动”。每个任务都有状态,每个线程都不超编,每个设备完成事件都会被及时收回来,这就是调度框架的核心呼吸节奏。

Θ=1max⁡(Twait,Tprepare,Trun,Treclaim)\displaystyle \Theta = \frac{1}{\max(T_{\rm wait}, T_{\rm prepare}, T_{\rm run}, T_{\rm reclaim})}
通俗说明:流水线上,出餐速度不取决于最快的师傅,而取决于最慢的工位;吞吐量是最慢工位耗时的倒数。
  1. 各环节耗时 \(T_{\rm wait}, T_{\rm prepare}, T_{\rm run}, T_{\rm reclaim}\)分别对应排队等待、数据准备、设备计算、结果回收四个阶段。
    Twait,Tprepare,Trun,TreclaimT_{\rm wait}, T_{\rm prepare}, T_{\rm run}, T_{\rm reclaim}
  2. 取最大值 \(\max\)当这些阶段可以重叠进行时,流水线的周期等于最慢环节耗时。
    max⁡(Twait,Tprepare,Trun,Treclaim)\max(T_{\rm wait}, T_{\rm prepare}, T_{\rm run}, T_{\rm reclaim})
  3. 取倒数得到吞吐量 \(\Theta\)每秒能完成多少轮推理,取决于最慢环节有多快。
    Θ=1max⁡(Twait,Tprepare,Trun,Treclaim)\Theta = \frac{1}{\max(T_{\rm wait}, T_{\rm prepare}, T_{\rm run}, T_{\rm reclaim})}

性能调优实践

后厨开业一个月,老陈开始翻台账:哪道菜平均出餐最慢?哪个灶台经常空着?哪台设备一直在发烧?性能调优的第一步不是盲目优化,而是先定位瓶颈。在边缘设备上,瓶颈往往藏在三个地方:算子耗时异常、数据搬运过大、设备利用率不均。

找到了瓶颈,就可以动手。批处理合并是边缘推理最常见的优化手段:把多个小的请求拼成一批,交给 GPU 或 NPU 一起算,摊薄内核启动和驱动开销。但批太大又会增加单次延迟,所以需要权衡。另一个重点是内存复用:与其每次推理都申请新缓冲区,不如维护一个内存池,让缓冲区反复使用,减少碎片和分配开销。最后还要关注功耗频率控制,因为 CPU、GPU、NPU 同时满负荷运行时,边缘设备会迅速过热降频,反而更慢。

批处理的加速比可以用下面的式子衡量:

老陈的台账越翻越薄,因为他学会了先瞄准最慢工位,再决定是加批、复用内存还是压频率。性能调优不是玄学,而是用数据替代拍脑袋。

η=N×t1tprep+⌈NB⌉×tB\displaystyle \eta = \frac{N \times t_1}{t_{\rm prep} + \left\lceil \frac{N}{B} \right\rceil \times t_B}
通俗说明:分子是不做批处理时一个个跑的总时间,分母是固定准备开销加批次运行开销;比值大于 1,说明批处理划算。
  1. 单条执行总时间 \(N \times t_1\)如果不合并,\(N\) 个请求要串行执行 \(N\) 次,每次耗时 \(t_1\)。
    N×t1N \times t_1
  2. 批次数 \(\lceil \frac{N}{B} \rceil\)\(N\) 个请求按批大小 \(B\) 分组,向上取整得到要跑几批。
    ⌈NB⌉\left\lceil \frac{N}{B} \right\rceil
  3. 批处理总耗时 \(t_{\rm prep} + \lceil \frac{N}{B} \rceil \times t_B\)固定准备时间加上每批的执行时间,就是合并后的总耗时。
    tprep+⌈NB⌉×tBt_{\rm prep} + \left\lceil \frac{N}{B} \right\rceil \times t_B
  4. 加速比 \(\eta\)两者相除,大于 1 时合并有效,小于 1 时说明批处理得不偿失。
    η=N×t1tprep+⌈NB⌉×tB\eta = \frac{N \times t_1}{t_{\rm prep} + \lceil \frac{N}{B} \rceil \times t_B}

边缘部署保障

最后一道关,是如何把调度系统稳稳送到真实场景。老陈发现外卖平台和堂食不一样:菜品要能打包、要能远程改菜单、还要在炖汤机坏了时不让客人干等。边缘部署保障正是这套“对外营业”能力。

模型格式转换适配,是把训练好的模型转成不同设备能吃的格式:CPU 吃 ONNX 或 TFLite,NPU 吃厂商的 IR,GPU 吃 OpenCL/Vulkan 内核。统一 API 封装,是让上层应用只调用一个推理接口,而不用关心底层到底是哪个设备。模型热切换,则是新版模型训练好后,不关机、不中断请求地完成替换。失败回退机制,是在 NPU 驱动崩溃或显存不足时,自动把任务降级到 CPU 或 GPU,保证系统还活着。

工程上常用多路径冗余来提高可用性。假设主路径和回退路径的成功率分别为 \(R_i\),多路径组合后的系统成功率是:

老陈的深夜食堂终于开成了连锁店。无论外面是暴雨还是断网,后厨只要有备用灶台和提前打包好的模型,就能保证客人吃上热乎饭。

Rsys=1−∏i=1n(1−Ri)\displaystyle R_{\rm sys} = 1 - \prod_{i=1}^{n}(1 - R_i)
通俗说明:只要有一条路能成功,系统就算成功。全部路径都失败的概率,是每条路径失败概率的乘积。
  1. 单条路径失败概率 \(1 - R_i\)\(R_i\) 是某条计算路径的成功率,例如 NPU 主路径成功率 0.95。
    1−Ri1 - R_i
  2. 所有路径同时失败 \(\prod_{i=1}^{n}(1 - R_i)\)假设路径互相独立,全体失败概率等于各自失败概率相乘。
    ∏i=1n(1−Ri)\prod_{i=1}^{n}(1 - R_i)
  3. 系统成功率 \(R_{\rm sys}\)用 1 减去“全部失败”的概率,就得到至少有一条路径成功的概率。
    Rsys=1−∏i=1n(1−Ri)R_{\rm sys} = 1 - \prod_{i=1}^{n}(1 - R_i)

AI 应用场景

园区智能摄像头盒子

阶段:多模型并发推理与异构调度

解决的问题:一个盒子要同时跑人脸检测、安全帽识别和车辆结构化三个模型。传统做法是排队串行推理,结果一有突发人流就跌到每秒一帧。通过调度框架按场景动态分配:人脸模型走 NPU,车辆模型走 GPU,安全帽模型走 CPU,并用优先级队列保证告警任务优先响应。

具体案例:某园区部署 20 路摄像头,边缘盒子上同时运行三个模型。调度器根据设备能力矩阵将卷积算子分给 NPU,把数据预处理和控制逻辑留在 CPU,高峰期平均时延下降约 40%。

机械臂实时视觉控制

阶段:计算后端选型与低延迟调度

解决的问题:机械臂抓取要求视觉推理延迟低于 10 毫秒,OpenCL 的命令提交开销可能让延迟超标。Vulkan 计算后端提供显式同步和更低的内核启动开销,配合紧耦合的事件循环,调度器能在一个控制周期内完成“取图—推理—下发动作”。

具体案例:某分拣机械臂使用 Vulkan 后端执行轻量分割模型,并把模型前处理放在 CPU、卷积放在 GPU、后处理再回到 CPU,形成一个三工位流水线,实测端到端延迟从 23 毫秒降到 9 毫秒。

多租户边缘网关

阶段:模型热切换与失败回退机制

解决的问题:边缘网关由多个租户共享,租户会随时上传新版本模型。通过统一 API 封装和版本化句柄,系统可以在旧模型未退出时加载新模型到 NPU,完成后无缝切换。若新模型在 NPU 上编译失败,则自动回退到 CPU 的 ONNX Runtime 后端。

具体案例:某智慧零售网关在夜间低峰期用调度框架完成模型热更新,更新期间零请求中断;NPU 驱动异常时自动降级,可用性从 90% 提升到 99.5%。

Python 演示

python69 行
from dataclasses import dataclass  @dataclassclass Device:    name: str    speed: dict    free_at: float = 0.0  @dataclassclass Task:    name: str    ops: dict    arrival: float = 0.0    priority: int = 1    device: str = ""    start: float = 0.0    end: float = 0.0  def estimate(task, device):    return sum(device.speed.get(op, 1.0) * n for op, n in task.ops.items())  class Scheduler:    def __init__(self, devices):        self.devices = devices        self.clock = 0.0        self.finished = []     def run(self, tasks):        ordered = sorted(tasks, key=lambda t: (t.arrival, -t.priority))        for task in ordered:            self.clock = max(self.clock, task.arrival)            best_dev = None            best_finish = float('inf')            for dev in self.devices:                duration = estimate(task, dev)                finish = max(dev.free_at, self.clock) + duration                if finish < best_finish:                    best_finish = finish                    best_dev = dev            task.device = best_dev.name            task.start = max(best_dev.free_at, self.clock)            task.end = task.start + estimate(task, best_dev)            best_dev.free_at = task.end            self.finished.append(task)  def main():    devices = [        Device("CPU", {"conv": 2.0, "fc": 1.5, "reshape": 0.5}),        Device("GPU", {"conv": 0.6, "fc": 0.8, "reshape": 0.4}),        Device("NPU", {"conv": 0.1, "fc": 0.3, "reshape": 1.2})    ]    tasks = [        Task("det_yolo", {"conv": 10, "fc": 2}, arrival=0.0, priority=3),        Task("seg_unet", {"conv": 20, "fc": 1}, arrival=0.5, priority=2),        Task("ocr_crnn", {"conv": 6, "fc": 4, "reshape": 2}, arrival=1.0, priority=1)    ]    scheduler = Scheduler(devices)    scheduler.run(tasks)    for task in scheduler.finished:        print(task.name, "->", task.device, "start=%.1f" % task.start, "end=%.1f" % task.end)  if __name__ == "__main__":    main()
目的:用 Python 模拟一个简单的异构调度器,根据每个设备的速度和任务到达时间,自动把模型推理任务分派给最早完成的 CPU、GPU 或 NPU。
  1. @dataclass自动为 Device 和 Task 生成初始化方法,减少样板代码。
  2. def estimate(task, device):按算子类型和数量估算任务在某设备上的计算耗时。
  3. ordered = sorted(tasks, key=lambda t: (t.arrival, -t.priority))先按到达时间排队,再按优先级降序决定调度顺序。
  4. finish = max(dev.free_at, self.clock) + duration计算“设备最早空闲时刻 + 本任务耗时”,也就是完成时间。
  5. if finish < best_finish:遍历所有设备,选择最早完成任务的设备,而不是最快但正在忙的设备。
  6. best_dev.free_at = task.end把设备占用时间更新到任务结束点,后续任务会看到这个未来空闲时刻。

常见误区

误区我的开发板宣传算力很高,只要把整个模型都塞进 NPU,一定比 CPU 和 GPU 都快。

正解NPU 只擅长卷积、矩阵乘这类密集计算。Reshape、Concat、动态循环这类算子,在 NPU 上可能很慢,甚至不支持。真正决定性能的是“计算耗时 + 数据搬运耗时”,而不是设备宣传的峰值 TOPS。

为何常见:厂商发布会总强调 NPU 的峰值算力,让人误以为峰值算力等于所有场景的真实速度。

误区OpenCL 是一种芯片,Vulkan 是跑在这个芯片上的驱动框架,所以买了不同的板子就要换不同后端。

正解两者都是跨平台的异构计算编程标准,不是硬件本身。同一块 GPU 既可以用 OpenCL,也可以用 Vulkan 编写计算内核。区别主要在于抽象层级、驱动支持和同步开销。

为何常见:OpenCL、Vulkan 这类名字听起来像厂商私有技术,加上驱动包经常以厂商命名,容易让人混淆软件标准和硬件设备。

误区既然有多模型并发,就应该为每个模型开一个线程,甚至一个模型开几十个线程,把任务全丢给设备。

正解GPU 和 NPU 的设备队列是有限的,大量线程只会增加上下文切换和内存争抢。调度框架应该使用有界线程池,配合任务队列和优先级控制,让设备保持“刚好装满但不过载”的状态。

为何常见:人们在多核 CPU 上习惯了“线程越多越好”的经验,忽略了异构设备本身的执行队列和带宽限制。

误区把模型从 100MB 压到 10MB,边缘设备上的推理速度就会自动快 10 倍。

正解端到端延迟包含内核启动、数据搬运、同步等待和算子执行。小模型如果被频繁启动,或者数据反复在 CPU 与 NPU 之间搬运,反而可能比大模型更慢。性能调优要看整条链路,而不是只看参数量。

为何常见:模型压缩的收益最直观,容易让人忽视调度开销和后端切换成本这些“隐形时间”。

面试题

Q1. 异构计算调度的核心目标是?

单选题 · easy

  • A.只让 CPU 承担全部推理任务
  • B.按设备特性分配合适算子← 正确答案
  • C.用云服务器替代边缘设备
  • D.关闭 NPU 以节省功耗

答案:B。异构计算调度不是让某一个设备包揽所有工作,而是像老陈派单一样,把每道工序交给最合适的师傅。CPU、GPU、NPU 各有擅长,按设备特性分配任务才是核心目标。

Q2. 边缘设备中 CPU 的主要特点是?

单选题 · easy

  • A.并行核多且功耗极低
  • B.通用性强但算力有限← 正确答案
  • C.只能执行卷积运算
  • D.不需要任何驱动支持

答案:B。CPU 就像什么都会做的全能主厨,能处理分支、控制和系统调用,但单核算力有限。它不适合承担大规模并行计算,更适合做调度、预处理和回退执行。

Q3. OpenCL 的本质是什么?

单选题 · easy

  • A.一种嵌入式操作系统
  • B.一款深度学习框架
  • C.一个异构计算编程标准← 正确答案
  • D.一种神经网络压缩格式

答案:C。OpenCL 是跨平台的异构计算编程标准,可以让同一份内核代码跑在不同设备上。它不是操作系统,也不是框架或压缩格式。

Q4. 以下哪类算子最适合交给 NPU?

单选题 · mid

  • A.动态形状的 while 循环
  • B.密集卷积与矩阵乘法← 正确答案
  • C.字符串正则表达式匹配
  • D.磁盘文件的随机读写

答案:B。NPU 内部堆满乘加阵列,最擅长密集卷积和矩阵乘法。动态循环、正则匹配和文件读写都属于控制流或 IO 类任务,更适合 CPU 处理。

Q5. 算子级设备映射最关键的是?

单选题 · mid

  • A.只看单算子理论算力
  • B.计算耗时加数据搬运开销← 正确答案
  • C.模型训练时的损失大小
  • D.开发工具的主题配色

答案:B。算子再快,如果搬运数据的时间比计算还长,整体收益依然是负的。设备映射必须同时考虑计算耗时和搬运开销,这也是能力矩阵存在的原因。

Q6. 多模型并发时,优先级队列主要用于?

单选题 · mid

  • A.提高模型参数压缩率
  • B.保证高优任务优先获得设备← 正确答案
  • C.增加相机采集帧率
  • D.将模型转换为 ONNX 格式

答案:B。优先级队列解决的是“谁先上灶台”的问题。高优先级请求,比如车辆告警,需要插队到日志分析这类低优先级任务之前。

Q7. 关于 Vulkan 计算后端的描述正确的是?

单选题 · hard

  • A.自动管理所有设备内存
  • B.显式同步带来更低延迟← 正确答案
  • C.完全不支持 GPU 计算
  • D.只能用于手机游戏渲染

答案:B。Vulkan 提供更底层的显式控制,包括命令缓冲、barrier 和同步,因而能压低启动和提交延迟。它同样可以用于通用 GPU 计算,但开发成本更高。

Q8. 后端选型时需要比较哪些开销?

多选题 · mid

  • A.内核启动与命令提交开销← 正确答案
  • B.主机与设备间数据拷贝开销← 正确答案
  • C.驱动支持和同步管理成本← 正确答案
  • D.模型训练时的 GPU 占用率

答案:ABC。选择 OpenCL 还是 Vulkan,要比较启动、拷贝、同步和驱动支持等实际工程开销。训练时的 GPU 占用率与推理后端选型没有直接关系。

Q9. 设备能力矩阵在调度中的作用包括?

多选题 · hard

  • A.记录算子在各设备上的耗时← 正确答案
  • B.计算数据搬运与执行总成本← 正确答案
  • C.作为动态设备选择依据← 正确答案
  • D.代替损失函数做反向传播

答案:ABC。设备能力矩阵是离线 benchmark 的结果表,用来估算算子耗时、搬运成本和最优设备选择。它只服务于推理调度,和模型训练的反向传播无关。

Q10. 根据 Amdahl 定律,异构调度提速上限取决于?

单选题 · extreme

  • A.并行设备的总数量
  • B.串行部分的时间占比← 正确答案
  • C.模型参数量的大小
  • D.OpenCL 版本号高低

答案:B。Amdahl 定律指出,无论并行设备加多少,总耗时都受串行部分制约。调度器首先要优化串行部分,比如控制逻辑和预处理,否则并行提速会被无限摊薄。