想象你走进一座音乐厅。台上是一支伟大的交响乐团,指挥家一挥棒,小提琴、铜管、定音鼓在同一个瞬间咬合,每个音符都精准踩在节拍器的心跳上——没有迟到的音,没有插队的音,分毫不差。这并非乐手们临场发挥的功劳,而是因为他们谱台上都放着一份写满精确时刻的乐谱:第几小节、第几拍、该用多大力奏响哪个音,早在无数次排练中就被刻进了肌肉记忆。 在AI芯片的世界里,也有一支这样的“乐团”,它叫LPU(Language Processing Unit,语言处理单元),专为人类语言这类“节奏感极强”的任务而生。传统芯片CPU/GPU更像是爵士乐队:乐手们上台后相互观察、随机应变,配合灵活,却难免错拍、停顿、节奏忽快忽慢。LPU的“指挥”——编译器——则选择了另一条路:在演出前就完成一切决策,把每个计算单元在哪个时钟周期做哪件事、数据走哪条通路,全部写成“乐谱”刻进硬件。演出开始后,硬件只需照谱演奏。 这就是LPU的确定性执行模型:把调度从运行时搬到编译期,让延迟变得像乐谱一样可精确预知。

岗位关联

掌握确定性执行模型,你将理解AI芯片如何用“编译期多花时间、运行期零抖动”的设计哲学,为LLM推理这类顺序主导负载带来极致稳定的性能——这是设计高性能AI加速器的核心思维。

目录
  1. 可视化演示
  2. 计算图调度基础
  3. 编译期静态化
  4. 确定性执行
  5. 编译调度算法
  6. 硬件支撑结构
  7. 动态调度对比
  8. 权衡与展望
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

动态展示LPU编译期静态调度指令在精确时间槽中按序执行的流水线,切换静态调度/WCET分析/零抖动/确定性多核四个视角观察时序可预测性。

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

计算图调度基础

一场音乐会的总谱,决定了每个乐器组何时进入、何时退出、如何交织。作曲家先画出一张“任务图”:弦乐铺垫、木管独奏、铜管回应、定音鼓收尾,各声部的先后关系在图纸上一目了然。AI计算也遵循同样的逻辑:模型在编译器中首先被表示成一张计算图。

计算图是深度学习模型在编译阶段的中间表示,节点代表算子(如矩阵乘、激活、访存),边代表数据依赖关系。调度(scheduling)要回答三个问题:每条指令在哪个执行单元上算?什么时候开始?数据走哪条路线?

传统芯片大多采用动态调度:硬件在运行时查看每条指令的就绪情况,临时决定发射顺序,像爵士乐手听到别人起调后临场发挥。好处是灵活,坏处是每次“听”和“决定”都要花费额外时间,且不同次的演奏节奏可能完全不同。LPU则选择静态调度:编译器读完整张计算图,一次性算出所有答案,编码进指令流和硬件配置中。

总谱画好了,接下来就看“指挥家”如何把这张图变成精确到拍的时刻表——这正是编译期静态化的任务。

G=(V,E),E={(u,v)∣u 必须先于 v 完成}G=(V,E),\quad E=\{(u,v)\mid u \text{ 必须先于 } v \text{ 完成}\}
这个公式是计算图的“总谱定义”:V是全部音符(指令),E是“谁必须在谁之前”的先后约束。一切调度算法都在这张图上展开。
  1. 节点集 $V$指全部指令的集合,每条指令是一个节点,相当于乐谱中的一个音符。
    V={v1,v2,…,vn}V=\{v_1,v_2,\dots,v_n\}
  2. 边集 $E$边 $(u,v)$ 表示 $u$ 的运算是 $v$ 的输入,$u$ 必须先于 $v$ 完成,就像“这个和弦必须等那个音符先结束”。
    E={(u,v)∣u∈V, v∈V, u precedes v}E=\{(u,v)\mid u\in V,\ v\in V,\ u \text{ precedes } v\}
  3. 前驱含义节点 $v$ 的前驱集合 $\mathrm{pred}(v)$ 是所有以 $v$ 为终点(箭头指向 $v$)的起始节点,它决定了 $v$ 最早能开始的时间。
    pred(v)={u∣(u,v)∈E}\mathrm{pred}(v)=\{u\mid(u,v)\in E\}

编译期静态化

排练房里,指挥家面前有一块白板:横轴是秒,纵轴是乐手座位。他要做的,是把每个音符“钉”在精确的时间点上——小提琴第10秒进,双簧管第12秒起,定音鼓第18秒砸下。订完之后,每个乐手甚至不需要看指挥,心里默数就能合拍。

LPU的编译器就是这位指挥家。它在编译期完成四件事:指令定序——通过拓扑排序确定指令的合法先后顺序,排除循环;时刻分配——为每条指令计算精确开始周期;通路规划——确定数据从生产者到消费者的固定物理路径,不允许运行时绕路;资源绑定——把指令绑定到具体的执行单元,并检查同一单元在时间上不被两指令同时占用。

这四件事共同构成“静态化”:运行时不再需要任何调度决策。调度结果是一张“时刻表”——每条指令的发射周期、执行单元、数据通路全部写死在芯片的配置里。这张时刻表怎么算?最核心的一步是“最早开始时间”(ASAP):一条指令只能等它所有前驱都完成后才能开始。

白板上的时刻表就是“乐谱”。排练可以无限反复,但演出只有一次——运行时,硬件就要照着这份乐谱分秒不差地执行。

ti=max⁡j∈pred(i)(tj+dj)t_i=\displaystyle\max_{j\in\mathrm{pred}(i)}\left(t_j+d_j\right)
这是“最早开始时间”公式:指令 $i$ 最早只能在所有前驱 $j$ 完成后开始。$t_j$ 是前驱的开始时刻,$d_j$ 是前驱耗时,取最大值即“排队最慢的那个乐手落座,我才能起手”。
  1. 前驱完成时间对每个前驱 $j$,它的完成时间等于开始时刻 $t_j$ 加上执行延迟 $d_j$。
    tj+djt_j+d_j
  2. 取最大值所有前驱中完成最晚的那个时刻,构成指令 $i$ 的最早可开始时间,即“所有合作的乐手里最后一个收手时,我才能起手”。
    max⁡j∈pred(i)(tj+dj)\max_{j\in\mathrm{pred}(i)}(t_j+d_j)
  3. 按拓扑序推进对图中每个节点按拓扑序依次套用公式,最终得到一张无冲突、可精确执行的全局时刻表。
    ti←max⁡j∈pred(i)(tj+dj),∀i∈Vt_i\leftarrow\max_{j\in\mathrm{pred}(i)}(t_j+d_j),\quad \forall i\in V

确定性执行

演出之夜。灯光暗下,指挥家轻轻起拍。乐手们并不互相张望彼此的谱面,更不猜测“他这段会不会拖拍”——排练已消灭了所有不确定性。每个人的心里只有一个节拍器,严格掐着拍子进入。你甚至可以说,即使指挥家突然晕倒,乐团也能凭乐谱完成整场演出。

LPU的运行时就是这样一支“无指挥乐团”。硬件按照编译期确定的指令序逐条发射,既不做数据冒险检测,也不做分支预测——因为编译期已经保证:指令发射时操作数一定就绪,数据通路固定,数据一定在预期周期到达。整个执行过程没有等待、没有重试、没有回退,这就是“零抖动延迟”。

延迟抖动(jitter)指同一条程序路径多次执行时延迟的变化量。在确定性执行模型中,这个变化量被严格压到零。对实时性敏感的应用而言,“每一次都一样快”比“偶尔极快”更有价值,因为最差情况即最好情况。

能做到这一步,光靠“指挥家”一个人不够——乐器、舞台、座位都必须完全配合乐谱。这套定制的舞台,就是硬件支撑结构。

Δt=tactual−texpected≡0\Delta t=t_{\text{actual}}-t_{\text{expected}}\equiv0
公式的意思是:实际延迟 $t_{\text{actual}}$ 恒等于预期延迟 $t_{\text{expected}}$,二者之差恒为0。对语音助手、自动驾驶这类系统,这意味着“最差情况和最好情况一样好”。
  1. 实际延迟 $t_{\text{actual}}$某条指令从发射到完成真实花掉的时钟周期数,由硬件计数器测量。
    tactualt_{\text{actual}}
  2. 预期延迟 $t_{\text{expected}}$编译期调度表为这条指令预留的周期数,即“乐谱上写的那个拍点”。
    texpectedt_{\text{expected}}
  3. 恒等零抖动$\equiv$ 表示恒等式,无论执行多少次,二者都相等,因此抖动严格为零。
    Δt=tactual−texpected≡0\Delta t=t_{\text{actual}}-t_{\text{expected}}\equiv0

编译调度算法

如果说指挥家的目标是画出一张完美乐谱,那他用到的“作曲法”就是我们这一节的主角:编译调度算法。它不像动态调度那样“到了台上临场看”,而是像一位精通编曲的大师,必须一次性规划好所有声部,不给自己留任何修改的机会。

以经典的列表调度(List Scheduling)为例,过程分四步。第一步,拓扑排序:把计算图中所有指令排成一个无环线性序列,任何指令都不会出现在它的依赖之前,相当于先确定剧本的出场次序。第二步,时间映射:按拓扑序逐个处理指令,用“最晚前驱完成时间”算出每条指令的最早开始时刻。第三步,资源分配:给每条指令分配一个执行单元,策略是“最早空闲优先”——先满足数据就绪,再在所有执行单元中挑最早空出来的那个。第四步,冲突消解:若两条指令竞争同一资源,就让它们的时间窗口完全错开。

乐谱画好了。但要让音符按乐谱精准发声,乐器必须先调准音——这就轮到硬件登场了。

∀i≠j,Ri∩Rj≠∅⇒ti+di≤tj 或 tj+dj≤ti\forall i\neq j,\quad R_i\cap R_j\neq\emptyset \Rightarrow t_i+d_i\le t_j \ \text{或}\ t_j+d_j\le t_i
公式描述的是“一个人不能同时做两件事”:两条指令若使用同一个执行单元(资源集合 $R$ 有交集),时间窗口必须完全错开,绝不允许重叠。
  1. 资源集合 $R_i$指令 $i$ 运行所需的硬件资源集合,如 ALU、访存端口、写回总线。
    Ri={ALU1,端口,… }R_i=\{\text{ALU}_1,\text{端口},\dots\}
  2. 冲突判定两条指令的资源集合有交集,说明它们会在物理上“抢同一个乐器”。
    Ri∩Rj≠∅R_i\cap R_j\neq\emptyset
  3. 时间错开要么 $i$ 在 $j$ 开始前结束($t_i+d_i\le t_j$),要么 $j$ 在 $i$ 开始前结束($t_j+d_j\le t_i$),两种取其一,保证资源独占。
    ti+di≤tj或tj+dj≤tit_i+d_i\le t_j\quad\text{或}\quad t_j+d_j\le t_i

硬件支撑结构

要让乐手“不看指挥也能合拍”,必须为乐团量身定制舞台:座位固定、乐器调音固定、指挥台到乐手的信号传递无延迟。LPU硬件团队做了三件关键的“定制”,用工程手段兑现编译期的确定性承诺。

第一件:专用SRAM,访存时刻固定。LPU把模型权重和中间结果放进片上专用SRAM,而不是延迟不确定的DRAM或动态缓存。SRAM访问延迟是精确常数,编译器能算出“数据在第几个周期到达ALU”,就像谱台永远不会卡纸。第二件:静态互连网络,数据通路固定。用静态配置的交叉开关和固定总线替代动态路由网络。传统芯片的数据包走哪条路线要到运行时仲裁,LPU则把路线在编译期就焊死——像管风琴的管子,每根只通向一个音管,气流一开准时发声。第三件:无分支设计,控制流固定。LLM推理图在编译期完全可知,层数、算子、激活函数全固定,因此LPU可以砍掉分支预测器和动态调度器,换成一组精确的时隙计数器。

舞台搭好了,乐团可以登场了。可为什么传统芯片不这么做?爵士乐队的即兴自由难道没有价值吗?接下来看这场“古典乐与爵士乐”的对决。

Tcritical=∑e∈p∗deT_{\text{critical}}=\sum_{e\in p^{*}} d_e
公式给出关键路径延迟:沿计算图中从输入到输出的最长路径 $p^{*}$,把所有指令延迟 $d_e$ 相加。这是整批任务的下限耗时,硬件的一切设计都是为了让这条路径的时间完全不差分毫。
  1. 关键路径 $p^{*}$计算图中从源点到汇点有多条路径,总延迟最长的那条叫关键路径,它决定整首曲子的最短时长。
    p∗=arg⁡max⁡p∑e∈pdep^{*}=\arg\max_{p}\sum_{e\in p} d_e
  2. 延迟求和把关键路径上每条指令的延迟 $d_e$ 累加,得到理论最短执行时间。
    ∑e∈p∗de\sum_{e\in p^{*}}d_e
  3. 执行下界任何调度方案都不可能快于 $T_{\text{critical}}$。确定性模型的目标是与这个下界的偏差恒为零,即“每个音都要弹满该弹的时值”。
    Tactual=TcriticalT_{\text{actual}}=T_{\text{critical}}

动态调度对比

爵士乐队的好处显而易见:没有乐谱也能接住各种风格,谁状态好谁多solo两段,灵活、即兴、充满现场感。代价是:状态波动导致节拍不稳,偶尔的抢拍拖拍要靠乐手现场找补,必须时刻用眼睛和耳朵互相监测。

传统CPU/GPU的动态调度正是如此。为了榨取运行时的信息,处理器引入了乱序执行、冒险检测、分支预测:每个周期都检查“这条指令的操作数到了没有”;没到就暂停流水线,插入气泡(stall);分支不确定就先猜,猜错了还要清空整条流水线重来。这些“临场监测与找补”全是开销,而且开销大小随输入变化,正是延迟抖动的来源。

LPU的静态调度把全部决策提前到编译期,运行时不检查、不预测、不重试。代价是灵活性降低——遇到编译期没见过的控制流,它就无能为力了。这是一个典型的工程取舍:要确定性的稳定,还是要动态的灵活。

动态调度胜在“什么曲子都能演”,LPU胜在“同一首曲子演一千遍分毫不差”。硬币的另一面,就是最后我们要看的权衡与展望。

Tdyn=Tideal+Nstall⋅tstall+Pmis⋅TflushT_{\text{dyn}}=T_{\text{ideal}}+N_{\text{stall}}\cdot t_{\text{stall}}+P_{\text{mis}}\cdot T_{\text{flush}}
这是动态调度的“成本账单”:理想执行时间 $T_{\text{ideal}}$ 之外,还要加上 stall 气泡个数乘以每个气泡的周期数,以及分支预测错误率乘以流水线冲刷代价。这些随机开销正是不可预测性之源。
  1. 理想时间 $T_{\text{ideal}}$假设所有依赖在发射时都已满足、所有分支预测都正确时的执行时间,是永远达不到的“梦想时间”。
    TidealT_{\text{ideal}}
  2. 气泡惩罚 $N_{\text{stall}}\cdot t_{\text{stall}}$每次数据冒险插入一个气泡,流水线空转 $t_{\text{stall}}$ 个周期,所有气泡累加起来就是总浪费。
    Nstall⋅tstallN_{\text{stall}}\cdot t_{\text{stall}}
  3. 分支惩罚 $P_{\text{mis}}\cdot T_{\text{flush}}$分支预测错误率 $P_{\text{mis}}$ 乘以重排整条流水线的代价 $T_{\text{flush}}$,该项是随机的,因此延迟出现抖动。
    Pmis⋅TflushP_{\text{mis}}\cdot T_{\text{flush}}

权衡与展望

没有哪种乐团永远最优——歌剧院的管弦乐团奏不了酒吧里的爵士,爵士乐队也撑不起马勒交响曲。LPU的确定性执行模型是硬币的一面,另一面是各种代价与适应边界。

适用负载方面,凡是执行图结构稳定、按序推进的重计算任务,LPU都能把确定性优势发挥到极限。最典型的就是LLM推理的顺序解码:生成第 $k+1$ 个 token 时,计算图与第 $k$ 个 token 完全同构,只是输入不同。同一首曲子反复演一千遍,把谱背得越熟越占便宜。性能取舍方面,确定性执行把运行期开销推向极低,却把大量工作压给编译器——编译时间更长、新模型适配更慢;一旦遇到动态形状(序列长度变化导致图结构变化),可能要重新编译甚至退回保守调度。架构局限方面,固定绑定的资源在负载结构漂移时可能闲置,利用率下滑。

LPU是一支为“语言”这个固定乐谱定制的乐团。它在LLM时代重新回答了一个经典问题:与其让硬件变得更聪明,不如让编译器把一切不确定性提前干掉。对未来的AI加速器设计而言,确定性执行模型提供了一条与动态调度截然不同的思路——在算力焦虑的时代,稳定性和可预测性本身就是一种巨大的性能。

U=∑i∈Vdi⋅riM⋅TtotalU=\frac{\sum_{i\in V} d_i\cdot r_i}{M\cdot T_{\text{total}}}
公式衡量芯片利用率:分子是全部指令的总计算量(延迟乘以占用资源数),分母是 $M$ 个执行单元在总执行时间内提供的总能力。$U$ 越接近 1,说明硬件被塞得越满。
  1. 计算量求和每条指令占用 $r_i$ 个资源单元并持续 $d_i$ 个周期,逐条累加得到总资源占用。
    ∑i∈Vdi⋅ri\sum_{i\in V}d_i\cdot r_i
  2. 总能力容量$M$ 个执行单元共同工作 $T_{\text{total}}$ 个周期,所提供的总资源时间。
    M⋅TtotalM\cdot T_{\text{total}}
  3. 利用率 $U$实际消耗除以总容量。确定性模型在负载匹配时 $U$ 逼近 1,负载漂移时下降——这就是它的“天花板与地板”。
    U=∑diriMTtotalU=\frac{\sum d_i r_i}{M T_{\text{total}}}

AI 应用场景

LLM推理服务加速

阶段:模型推理与部署优化

解决的问题:传统GPU处理多用户并发LLM推理时,线程调度抖动使token生成延迟忽高忽低——P99远大于P50,用户感到“时快时卡”。LPU的确定性执行消除了调度抖动,让每个token的生成延迟恒定可预测。

具体案例:Groq的LPU在运行Llama系列模型时,每秒钟可输出数百个token,且延迟分布极窄。交互式AI应用因此能保持稳定响应节奏,不再出现“一会儿秒回、一会儿转圈”的体验落差。

实时语音交互系统

阶段:端到端延迟优化与实时链路建设

解决的问题:语音助手对“说完就答”的节奏极度敏感。动态调度芯片可能出现这轮响应快、下轮响应慢的随机波动,而确定性模型让每次响应的延迟都严格等于编译期预算值。

具体案例:智能客服语音机器人需要稳定的首字延迟(TTFB)控制在200ms内。采用确定性执行模型后,系统可以精确规划每一级模块的延迟预算,从麦克风采集到语音合成全链路延迟可预测,彻底告别卡顿感。

确定性流式数据处理

阶段:数据预处理与特征工程管线

解决的问题:工业控制和高频交易中的AI算子需要硬实时保证——最坏情况执行时间(WCET)必须可证明、有上界。动态调度无法给出严格延迟界,确定性执行模型则天然满足。

具体案例:在智能制造产线中,缺陷检测模型必须在每个工件经过相机视场的8ms窗口内完成推理。LPU利用确定性调度保证推理延迟恒定为6ms,余量2ms,一次都不超时,产线因此无需增加缓冲工位。

Python 演示

python66 行
"""确定性执行模型演示: 列表调度器(List Scheduling)"""from collections import deque class ListScheduler:    """在编译期为每条指令确定执行单元与精确开始时刻"""     def __init__(self, num_units):        self.num_units = num_units        self.resource_free = [0] * num_units  # 每个执行单元的下一个空闲时刻        self.schedule_map = []     @staticmethod    def topo_sort(graph):        # graph: {节点: [后继节点]}, 统计每个节点的前驱数量(入度)        in_degree = {node: 0 for node in graph}        for succs in graph.values():            for succ in succs:                in_degree[succ] = in_degree.get(succ, 0) + 1        ready = deque([n for n, d in in_degree.items() if d == 0])        order = []        while ready:            node = ready.popleft()            order.append(node)            for succ in graph.get(node, []):                in_degree[succ] -= 1                if in_degree[succ] == 0:                    ready.append(succ)        return order     def list_schedule(self, graph, delay_map):        preds_map = {node: [] for node in graph}        for pred, succs in graph.items():            for succ in succs:                preds_map[succ].append(pred)        topo = self.topo_sort(graph)        start_time = {}        finish_time = {}        for node in topo:            ready_time = 0            for pred in preds_map[node]:                ready_time = max(ready_time, finish_time.get(pred, 0))            best_unit, best_start = 0, None            for unit in range(self.num_units):                s = max(ready_time, self.resource_free[unit])                if best_start is None or s < best_start:                    best_unit, best_start = unit, s            self.resource_free[best_unit] = best_start + delay_map[node]            start_time[node] = best_start            finish_time[node] = best_start + delay_map[node]            self.schedule_map.append((node, best_unit, best_start))        return self.schedule_map  if __name__ == "__main__":    # 依赖图: A_LD -> B_ADD / C_MUL, B_ADD/C_MUL -> D_ST    graph = {        "A_LD": ["B_ADD", "C_MUL"],        "B_ADD": ["D_ST"],        "C_MUL": ["D_ST"],        "D_ST": []    }    delays = {"A_LD": 2, "B_ADD": 2, "C_MUL": 3, "D_ST": 1}    scheduler = ListScheduler(num_units=4)    result = scheduler.list_schedule(graph, delays)    for node, unit, t in result:        print("指令 {:4s} -> 单元{}, 时刻 {}".format(node, unit, t))
目的:用列表调度算法演示确定性执行模型的核心——编译器(调度器)在运行前为每条指令分配好执行单元和开始时刻,生成一张可精确执行的静态时刻表。
  1. in_degree[succ] = in_degree.get(succ, 0) + 1遍历后继表统计每个节点入度,入度为0表示没有前驱依赖,可最先调度。
  2. order.append(node)把入度归零的节点收入拓扑序,保证调度顺序永远合法。
  3. ready_time = max(ready_time, finish_time.get(pred, 0))数据就绪时间取所有前驱完成时刻的最大值,等价于公式 $t_i=\max(t_j+d_j)$。
  4. s = max(ready_time, self.resource_free[unit])发射时刻必须同时满足数据就绪和执行单元空闲,两者中取较晚者。
  5. self.resource_free[best_unit] = best_start + delay_map[node]把执行单元的占用期更新到本次指令结束,后续指令不得与之重叠。

常见误区

误区很多人看到“确定性执行”就以为LPU是单核顺序执行,一条一条慢慢算,只是延迟稳定而已。

正解确定性 ≠ 串行。编译期静态调度恰恰以并行为目标:多个执行单元在同一个时钟周期各司其职,指令块密集铺满时间轴。确定性指的是“并行安排是提前定死的”,而不是“不并行”。可视化演示中可以看到,四个执行单元在同一周期同时工作。

为何常见:因为“确定性”容易让人联想到“一条路走到黑”,而且静态调度确实不像动态调度那样运行时到处找并行机会,所以被误以为没有并行。

误区以为编译器把所有工作做完后,硬件就是一个“傻执行”的管道,几乎不需要控制逻辑。

正解省去的只是动态调度器、冒险检测、分支预测这一整套“临场决策”电路。硬件仍然需要精确的时隙计数器、互连网络配置逻辑、SRAM读写控制器来兑现编译期的承诺。节省的是“探索和纠错”的电路,不是全部控制。

为何常见:人们把“编译器多干活”过度理解为“硬件不用干活”——但乐手不用看指挥,不代表乐手不需要乐器。

误区认为确定性执行模型下指令之间没有任何等待,每个周期都在疯狂计算。

正解数据依赖的等待依然存在,只是这些等待在编译期就被预算好并写进了时刻表。执行中不“意外等待”,但“计划内等待”是确定排列的——正如乐手在第4小节等第3小节的和弦收完,这种等待被编排进了乐谱,不产生抖动。

为何常见:零抖动(zero jitter)听起来就像零等待(zero latency),两个词都有“零”字,容易混淆。

误区看到LPU指标惊艳,就认为确定性执行模型在所有负载下都碾压CPU/GPU。

正解确定性执行的优势集中在“图结构稳定”的负载上,此时编译成本被海量重复执行分摊,且无动态开销。一旦负载结构频繁变化,LPU需要重新编译甚至无法完美静态化,此时动态调度反而更适应。两者是不同赛道,不存在绝对优劣。

为何常见:芯片厂商宣传时只展示自己擅长的基准测试,小白容易把“适配场景的最优”当成“所有场景的标配”。

误区认为确定性执行模型彻底消灭了所有分支,硬件里没有任何分支处理能力。

正解LPU面对的是LLM推理这类控制流确定的负载——模型层数、算子、形状在编译期已知,因此不需要分支预测。但这不代表所有程序都不需要。若遇到数据相关的动态分支(如动态形状、条件跳转),编译期无法静态化,就必须退回通用处理或重新编译。“无分支设计”是负载特性使然,而非硬件做了绝对保证。

为何常见:把“当前的负载恰好无分支”误解成“芯片永不需要分支”,忽略了确定性模型的适用范围边界。

面试题

Q1. LPU确定性执行模型的核心特征是什么?

单选题 · easy

  • A.运行时动态检查数据冒险
  • B.编译期确定指令执行时刻← 正确答案
  • C.使用共享内存降低访存
  • D.用线程池调度计算任务

答案:B。确定性执行把调度决策从运行时搬到编译期,每条指令的时刻、通路在编译期已定。A是动态调度特征,C是内存优化手段,D是软件调度而非芯片调度。

Q2. 下列哪项是确定性执行带来的直接收益?

单选题 · easy

  • A.更高的芯片主频
  • B.零抖动延迟保证← 正确答案
  • C.更小的硅片面积
  • D.更低的编译开销

答案:B。确定性执行保证每次执行延迟完全一致(零抖动),这正是LPU最核心的卖点。主频和面积不是直接收益——编译开销反而是增大的。

Q3. 静态调度与动态调度的根本差异在于?

单选题 · mid

  • A.静态调度不产生任何等待
  • B.动态调度是运行时决策← 正确答案
  • C.静态调度没有并行能力
  • D.动态调度不涉及乱序执行

答案:B。动态调度靠硬件在运行时观察数据就绪情况做决策;静态调度在编译期一次性算好。这才是根本区分。A夸大了静态调度的能力(等待仍在,只是被预算好),C和D都与事实相反。

Q4. 确定性执行模型中,为何可以省去冒险检测?

单选题 · mid

  • A.编译期已保证时序无冲突← 正确答案
  • B.硬件不再存在数据相关
  • C.所有指令都相互独立
  • D.指令发射速度极大提升

答案:A。编译期调度已将指令发射时刻错开,保证操作数在需要时一定就绪,因此冒险检测失去意义。B和C是误解——数据相关依然存在,只是被提前安排好,像乐谱里和弦的连接顺序依旧存在,但不需临场找补。

Q5. 列表调度算法计算指令就绪时间时采用的准则是?

单选题 · hard

  • A.所有前驱完成时刻的最大值← 正确答案
  • B.所有前驱完成时刻的最小值
  • C.前驱开始时刻的加权平均
  • D.资源空闲时刻加指令延迟

答案:A。就绪时间 = max(所有前驱完成时刻),取最慢的那个前驱。B会导致前驱未完成就开始,C没有物理意义,D混淆了数据就绪与资源空闲两个独立概念。

Q6. 动态调度中插入气泡(stall)的根本原因是什么?

单选题 · extreme

  • A.运行时发现依赖未满足← 正确答案
  • B.编译期未考虑所有依赖
  • C.执行单元数量不够
  • D.时钟频率无法匹配

答案:A。气泡的本质是“发射端口到了,但数据还没到”。动态调度器在运行时发现RAW(写后读)依赖未满足,只能插入气泡等待。C是结构冒险,属于另一类等待;B和D与气泡产生机制无关。

Q7. LPU的确定性执行最适合哪类负载?

单选题 · easy

  • A.动态形状的稀疏计算
  • B.顺序解码的LLM推理← 正确答案
  • C.高分支概率的随机程序
  • D.频繁修改图结构的任务

答案:B。LLM推理中每个token的解码按同一张计算图顺序执行,结构完全可预知,确定性执行能最大化优势。其余选项的负载结构编译期难以静态化,无法发挥确定性模型的长处。

Q8. 确定性执行模型需要哪些硬件支撑?

多选题 · mid

  • A.专用SRAM存储权重← 正确答案
  • B.静态互连网络通路← 正确答案
  • C.乱序执行控制逻辑
  • D.预定时隙计数机制← 正确答案

答案:ABD。SRAM提供确定访存延迟,静态网络提供确定数据通路,时隙计数提供确定控制节奏,三者共同兑现确定性承诺。乱序执行正是确定性模型要砍掉的动态机制,因而是不需要的。

Q9. 与动态调度相比,确定性执行模型有哪些特点?

多选题 · mid

  • A.延迟可预测性更高← 正确答案
  • B.运行灵活性更低← 正确答案
  • C.硬件控制开销更小← 正确答案
  • D.运行时无需分支预测← 正确答案

答案:ABCD。确定性模型以“编译期多算”换“运行期简单”,因此延迟可预测、运行灵活性低、省去动态控制逻辑、无需分支预测。四个选项全部成立,这是一个全面的权衡。

Q10. 下列关于确定性执行局限的说法,正确的有哪些?

多选题 · hard

  • A.编译期开销通常更大← 正确答案
  • B.动态分支负载难以适配← 正确答案
  • C.无法并行执行多条指令
  • D.对负载结构性要求较高← 正确答案

答案:ABD。静态调度需全局搜索分析,编译更耗时;负载结构变化时需重新编译或退回保守调度,因此要求负载结构稳定。C是错误说法——确定性执行恰恰依赖编译期静态并行,多个执行单元同时工作。