Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

第 8 章:仿射调度与多维时间

直觉

第 7 章把程序真正必须保持的跨实例顺序汇总为依赖关系

$$ \Delta_{S\to T}\subseteq D_S\times D_T. $$

其中第一元是 source,第二元是 sink。调度的任务不是重新猜测值从哪里来,而是给每个语句实例分配一个新时间戳,并保证每条已有依赖仍从早指向晚。

一个时间戳可以有多维。例如 $(3,5)$ 早于 $(4,-100)$,因为词典序只查看第一个不同分量;第二分量再小也不能推翻第一分量已经确定的先后。因此,多维调度的合法性不是“每一维都严格递增”,而是“第一个非零距离分量为正”。这个区别正是循环交换、融合、倾斜和并行维构造的基础。

仿射调度把时间戳限制为循环变量和全局参数的仿射函数。限制看似很强,却把寻找循环变换的问题变成寻找有限多个系数的问题。第 9 章会继续说明,如何用 affine Farkas 引理把“对所有依赖实例成立”也变成有限约束。

本章始终分开两个问题:

  1. 合法性:所有 source → sink 依赖是否严格向前;
  2. 性能:合法候选中,哪个更利于并行、局部性、向量化或 tiling。

前者是必须满足的语义边界,后者才是优化选择。

形式定义

一维与多维仿射调度

令语句 $S$ 的迭代向量为 $x\in D_S(p)\subseteq\mathbb Z^{d_S}$,参数向量为 $p\in\mathbb Z^q$,参数上下文为 $C(p)$。一维仿射调度写成

$$ \theta_S(x,p)=C_Sx+E_Sp+c_S\in\mathbb Z. $$

这里 $C_S$ 是迭代变量的系数行向量,$E_S$ 是参数的系数行向量,$c_S$ 是常数;它们是待求的调度系数。$E_S$ 使用尚未被域记号占用的字母,因而不会与迭代域 $D_S(p)$ 混淆;符号 $C_S$ 也不同于参数上下文 $C(p)$。要求整数时间戳时,这些系数取整数;也可先求有理系数,再按保持顺序的统一正尺度清除分母。

一个 $k$ 维仿射调度为

$$ \Theta_S(x,p) =\bigl(\theta_S^{(1)}(x,p),\ldots,\theta_S^{(k)}(x,p)\bigr) \in\mathbb Z^k, $$

每个分量各有一组仿射系数。不同语句的系数可以不同,因此常数分量可以表达同一循环坐标处的语句顺序。

合法性的固定方向:source 严格早于 sink

对每条依赖 $(x,y)\in\Delta_{S\to T}(p)$,合法调度必须满足

$$ \boxed{ \Theta_S(x,p)\mathrel{\mathrm{lex}<}\Theta_T(y,p) } $$

即 source 的时间戳严格小于 sink 的时间戳。不能交换两端后仍称作同一方向。

一维时定义调度差

$$ d(x,y,p)=\theta_T(y,p)-\theta_S(x,p). $$

若时间戳属于整数,则严格先后等价于

$$ d(x,y,p)\ge1. $$

“至少 1”使用了整数刻度;若尚在未归一化的有理时间上求解,只能先写 $d>0$,再统一清分母或规定尺度,不能无条件把任意正有理数说成至少 1。

词典序的逐层语义

对多维调度,令第 $r$ 维的距离为

$$ d_r(x,y,p) =\theta_T^{(r)}(y,p)-\theta_S^{(r)}(x,p). $$

从某条依赖关系或其一个非空分支开始,记尚未被先前维度严格承载的实例集合为 $U_0=\Delta_{S\to T}$。第 $r$ 层只在当前残余集合 $U_{r-1}$ 上要求

$$ \forall(x,y)\in U_{r-1}:\quad d_r(x,y,p)\ge0. $$

这叫本维对残余依赖的 weak satisfaction(弱满足)。其中

$$ K_r=U_{r-1}\cap\{(x,y)\mid d_r(x,y,p)\ge1\} $$

中的整数依赖实例在前面各维距离都为零,而在第 $r$ 维严格为正,所以由本维 strictly carried(严格承载)。距离仍为零的实例形成下一层残余关系

$$ U_r=U_{r-1}\cap\{(x,y)\mid d_r(x,y,p)=0\}. $$

若最终 $U_k=\varnothing$,每条依赖都在某个最早非零维得到正距离,因而整个向量严格词典序合法。对已经属于某个 $K_r$ 的依赖,后续距离可以为正、零或负,因为词典序先后已经由第 $r$ 维决定。于是下面三个术语必须分开:

  • weak satisfaction:当前残余关系上本维距离非负,允许为零;
  • strict carry:此前各维为零、本维距离至少为 1 的整数依赖实例;
  • residual dependence:此前各维距离都为零,尚须交给下一维处理的实例。

Feautrier 的 Part I 研究一维时间及其约束化;Part II 把算法扩展到词典序多维时间,并按需要构造足够的调度维数。本节的 $U_r$ 写法是为突出逐层语义所作的教学性整理。

固定候选的语义与逐维合成的顺序

上面的 $K_r,U_r$ 首先定义了一个已经固定的多维候选调度如何承载依赖:此时每个 $d_r$ 的系数已知,集合交可以直接计算。自动合成时,当前维的 $C_S^{(r)},E_S^{(r)},c_S^{(r)}$ 尚未知,不能把含这些未知系数的 $d_r=0$ 立即当成下一层 Farkas 的已知前提。

本教程采用以下顺序流程:

  1. 第 $r$ 轮开始时,$U_{r-1}$ 已知;$r=1$ 时它就是输入依赖 $U_0$,以后各轮则来自已经固定的前一维。
  2. 只在这个已知 $U_{r-1}$ 上,为当前 $d_r\ge0$ 建立 Farkas 约束,求出当前维,并按该轮的进展或性能准则选定一组系数。
  3. 对本轮涉及的每个语句 $S$,固定 $C_S^{(r)},E_S^{(r)},c_S^{(r)}$。代入后 $d_r$ 成为已知仿射式,再计算 $K_r=U_{r-1}\cap\{d_r\ge1\}$ 与 $U_r=U_{r-1}\cap\{d_r=0\}$。
  4. 若 $U_r\ne\varnothing$,把这个已知的新 residual dependence 化为下一轮可处理的分支或多面体描述,再为第 $r+1$ 维重新建立有限系统;若为空,严格词典序构造结束。

顺序中的“先固定、再求 $U_r$”是线性有限化的必要边界。若同时求所有维,下一层残余域的约束矩阵会含上一维未知调度系数;它再与下一层 Farkas 乘子相乘,出现“未知调度系数 × 未知乘子”的双线性项。普通 LP/ILP 不会仅因变量声明为整数就自动线性化这种乘积。联合求多维调度需要另行设计带有效界的离散或线性化编码;本教程没有给出、也不声称当前公式已经给出这种联合 ILP。

Permutable band 与 coincident 维

一个 band 是准备作为整体处理的一组连续调度维。仅有整个时间向量 lex-positive,并不足以任意交换这些维:某条依赖可能靠较早分量的正值掩盖较晚分量的负值,一旦交换,负值就可能先出现。

在常用的教学性判据下,先只看进入 band 时尚未由外层严格承载的活动依赖:若它们在 band 内每一维的距离都非负,这些维可构成 permutable band。其中在 band 内出现正分量的依赖由 band 承载;band 内距离全为零的依赖仍可留给后续维处理。分量非负保证交换 band 内的循环维不会让活动依赖以负分量开头;这种结构也是矩形 tiling 的重要入口。实际调度树还要结合依赖过滤、语句域和工具对 band 的具体定义。

若某一调度维对进入该维时仍活动、会限制并行执行的相关依赖距离都为零,则称该维 coincident;在没有归约、同步、别名等额外障碍时,它可对应并行循环维。coincident 指依赖距离为零,不是“该维调度系数全部为零”,也不能只检查一条依赖便下结论。

手算示例

一维 shift:identity、reverse 与 constant 逐条验证

沿用第 7 章的精确 RAW 关系:

$$ \Delta_{S\to S} =\{[i]_S\to[i+1]_S\mid1\le i<N-1\}. $$

对任意 source $i$ 和 sink $i+1$,逐一验证三个一维候选。

Identity schedule 取 $\theta(i)=i$。调度差为

$$ \theta(i+1)-\theta(i)=(i+1)-i=1\ge1. $$

所以每条 source → sink 依赖都严格向前,identity 合法。

Reverse schedule 取 $\theta(i)=-i$。调度差为

$$ \theta(i+1)-\theta(i)=-(i+1)-(-i)=-1<1. $$

它让 sink 早于 source,reverse 非法。

Constant schedule 取 $\theta(i)=0$。调度差为

$$ \theta(i+1)-\theta(i)=0. $$

两端时间戳相同,无法区分这条已有依赖的先后,所以作为完整一维调度非法。常数分量并非总无用:它可以是多维调度中允许并列、交给后续维处理的弱满足维,也可区分不同语句,例如 $\theta_P(i)=0,\theta_C(i)=1$。这里的非法结论来自 shift 确实存在自依赖,而不是“任何两个相同时间戳都无条件非法”。

生产者—消费者:sequence 与 fusion 都可以合法

设各个 $i$ 之间独立,唯一依赖为

$$ \Delta_{P\to C} =\{[i]_P\to[i]_C\mid0\le i<N\}. $$

一种分阶段的 sequence/distribution 风格调度是

$$ \Theta_P(i)=(0,i),\qquad \Theta_C(i)=(1,i). $$

每条依赖的距离为 $(1,0)$,第一维严格承载它;所有 producer 阶段在所有 consumer 阶段之前。

另一种逐元素融合调度是

$$ \Theta_P(i)=(i,0),\qquad \Theta_C(i)=(i,1). $$

每条依赖的距离为 $(0,1)$:第一维弱满足且留下全部残余依赖,第二维再严格承载。它产生 $P(0),C(0),P(1),C(1),\ldots$ 的顺序。

两个调度都满足同一 source → sink 严格词典序约束,但性能取舍不同。融合通常缩短中间值复用距离并减小工作集;分阶段则可能更利于每个阶段独立向量化、批处理或采用不同并行策略。融合还可能增加寄存器压力或把资源需求不同的两个 kernel 绑在一起。因此,合法性没有宣布哪一个更快。

对融合调度,第一维在这组依赖上的距离恒为零;若确实不存在跨 $i$ 的其他依赖,它是 coincident 候选,可让不同 $i$ 并行。第二维仅编码同一 $i$ 内 producer 在 consumer 之前。

二维循环交换:要检查变换后的第一个非零距离

考虑二维语句实例 $S(i,j)$,并有依赖

$$ \Delta =\{[i,j]_S\to[i+1,j-1]_S \mid0\le i<I-1\land1\le j<J\}. $$

原 identity 调度 $\Theta(i,j)=(i,j)$ 的距离为

$$ (i+1,j-1)-(i,j)=(1,-1). $$

虽然第二分量为负,第一非零分量是 $+1$,所以它严格词典序合法。这也直接反驳“合法要求每维都非负或严格递增”。

交换两个循环相当于候选 $\Theta'(i,j)=(j,i)$。同一依赖的距离变为

$$ (j-1,i+1)-(j,i)=(-1,1). $$

第一个非零分量是 $-1$,故 interchange 非法。若程序只有 $[i,j]\to[i,j+1]$ 这样的内层前向依赖,则交换后距离从 $(0,1)$ 变成 $(1,0)$,仍合法。循环交换本身既非总合法也非总非法;答案由完整依赖关系决定。

主 stencil:skewing 把负的空间分量变为可置换结构

主案例的三个依赖分支为

$$ \Delta_\delta =\{[t-1,i+\delta]_S\to[t,i]_S \mid \delta\in\{-1,0,1\}\ \land\ \text{两端均在域内}\}. $$

identity 调度 $\Theta(t,i)=(t,i)$ 下,从 source $(t-1,i+\delta)$ 到 sink $(t,i)$ 的距离是

$$ (1,-\delta). $$

三个分支分别得到 $(1,1),(1,0),(1,-1)$。它们都 lex-positive,因为时间维首先增加 1;但最后一个分支的空间距离为负,所以 $(t,i)$ 两维不满足上述 permutable band 的分量非负充分条件,直接交换也会出错。

取倾斜调度

$$ \Theta'(t,i)=(t,t+i). $$

则距离变成

$$ \begin{aligned} \Theta'(t,i)-\Theta'(t-1,i+\delta) &=(t,t+i)-(t-1,t-1+i+\delta)\\ &=(1,1-\delta). \end{aligned} $$

对 $\delta=-1,0,1$,分别是 $(1,2),(1,1),(1,0)$,每个分量都非负,且第一分量严格为正。于是这两个调度维形成适合交换与矩形 tiling 的非负距离结构。skewing 没有删除依赖;它改变坐标,使 wavefront 的合法顺序更容易组织成可置换 band。是否真的更快仍取决于 tile 大小、边界代码、并行粒度和存储层次。

编译器用途

  1. 把依赖变成调度可行域。 对第 7 章产生的每个 $\Delta_{S\to T}$,编译器建立 source 早于 sink 的约束;第 9 章将说明如何有限化其中的全称量词。
  2. 组织 sequence 与 fusion。 不同语句的常数和循环系数决定它们是分阶段执行,还是在共同循环坐标下交错执行。
  3. 识别 interchange 与 skewing。 变换后重新计算依赖距离;严格词典序检查决定语义合法性,分量非负结构进一步决定 band 是否适合置换和 tiling。
  4. 逐层暴露并行性。 一维调度严格承载一部分依赖后,调度器先固定该维、计算已知残余关系,再为下一维重新建模;某维对所有相关依赖距离为零时,才可能标为 coincident。
  5. 保留参数化结构。 $E_Sp$ 让调度随合法参数上下文变化,但参数条件必须与依赖域一起参与全称合法性检查。
  6. 传给后续代码生成。 合法的调度不是最终循环文本。编译器还须扫描调度像、生成边界与 guard,并保持语句内部事件语义。

Feautrier Part IPart II 给出仿射调度的经典算法路线。Pluto 项目与作者资料 展示了面向并行性和局部性的自动仿射变换实践。它们都在合法域上进一步选择调度,而不是用性能目标代替依赖合法性。

常见误区

  1. 把方向写成 sink → source。 固定条件是 $\Theta_S(x)\mathrel{\mathrm{lex}<}\Theta_T(y)$,其中 $(x,y)\in\Delta_{S\to T}$。
  2. 要求每一维都严格递增。 严格词典序只要求第一个非零距离为正;之前各维为零,之后各维不再决定该依赖。
  3. 把 weak satisfaction 当作已经完成合法性。 $d_r=0$ 的实例仍在残余关系中,必须由后续维严格承载。
  4. 认为较晚维也必须非负。 对已经由更早维严格承载的依赖,较晚维可为负;只有构造 permutable band 时才另加更强的非负结构。
  5. 把 permutable 等同于 lex-positive。 lex-positive 允许 $(1,-1)$,但交换两维会得到非法的 $(-1,1)$。
  6. 把 coincident 解释为系数为零。 判断对象是所有相关依赖的该维距离,还要排除归约、同步和别名等其他限制。
  7. 宣布 constant schedule 总非法。 对 shift 自依赖,完整一维常数调度确实非法;作为多维前缀或跨语句常数顺序,它可以有合法用途。
  8. 对未归一化有理时间直接写差至少 1。 $>0$ 到 $\ge1$ 依赖整数时间戳或清分母后的尺度约定。
  9. 把合法调度当成高性能调度。 sequence 与 fusion 可以同时合法,却有不同的局部性、并行和资源取舍。
  10. 只检查一个代表距离。 参数边界、析取分支和不同语句依赖都必须纳入完整关系,不能用一个平均距离替代全称检查。
  11. 把未知 $U_r$ 直接送进下一层 Farkas。 合成时必须先解出并固定第 $r$ 维,才能让 $d_r=0$ 成为已知线性约束;否则下一层乘子会与上一层未知调度系数形成双线性乘积。

练习

练习 EX08-B01|Shift 候选调度合法性(基础)

对 shift 关系 $[i]\to[i+1]$,分别检验 $\theta(i)=2i+3$、$\theta(i)=-2i$、$\theta(i)=7$。统一计算 $\theta(\mathrm{sink})-\theta(\mathrm{source})$,并判定一维合法性。

答案索引: ANS-EX08-B01

练习 EX08-B02|Producer/consumer 常数次序(基础)

对 $P(i)\to C(i)$,验证 $\Theta_P=(i,1),\Theta_C=(i,0)$ 为何非法;再只改一个常数使它合法。

答案索引: ANS-EX08-B02

练习 EX08-D01|Lex-positive 与 permutable band(推导)

取二维距离 $(1,-3)$。说明它为何 lex-positive,却不能直接证明两维构成 permutable band;交换两维后重新判断。

答案索引: ANS-EX08-D01

练习 EX08-D02|Residual dependence 分层(推导)

设已知残余关系 $U_0$ 被第一维距离分成 $d_1=1$ 与 $d_1=0$ 两类,且在 $d_1=0$ 类上 $d_2=2$。只用集合条件写出 $K_1,U_1,K_2,U_2$,说明哪些依赖由哪一维严格承载;不要求枚举未给出的具体实例。

答案索引: ANS-EX08-D02

练习 EX08-C01|两支依赖下的 identity/interchange(综合)

对依赖 $[i,j]\to[i,j+1]$ 和 $[i,j]\to[i+1,j-2]$ 的并,分别检查调度 $(i,j)$ 与 $(j,i)$;不能只检查其中一个分支。

答案索引: ANS-EX08-C01

练习 EX08-D03|Stencil skew 系数下界(推导)

将 stencil 的倾斜推广为 $\Theta_k(t,i)=(t,kt+i)$。求三个 source-to-sink 距离 $(1,k-\delta)$,给出使第二分量对所有 $\delta\in\{-1,0,1\}$ 非负的最小整数 $k$。

答案索引: ANS-EX08-D03

练习 EX08-C02|Sequence 与 fusion 的性能权衡(综合)

为逐元素 $P(i)\to C(i)$ 比较分阶段与融合调度:分别列出 $N=3$ 时的执行顺序,并各写一个可能的性能优势和一个代价。不得把合法性直接当作加速结论。

答案索引: ANS-EX08-C02

练习 EX08-D04|后续负维不推翻词典序合法性(推导)

构造一个两维调度,使某条依赖第一维距离为 0、第二维为 1;再添加第三维距离 $-100$,解释为何完整调度仍合法。

答案索引: ANS-EX08-D04

本章小结

  • 仿射调度把语句实例和参数映到整数时间戳;合法性始终按 source → sink 写成 $\Theta_S(x)\mathrel{\mathrm{lex}<}\Theta_T(y)$。
  • 对整数一维时间,严格先后等价于 sink 减 source 至少为 1;identity shift 合法,reverse 与完整 constant schedule 非法。
  • 多维调度逐层处理残余依赖:本维先 weakly satisfy,正距离实例由本维 strictly carry,零距离实例继续进入 residual dependence。
  • 验证固定候选时可直接定义所有 $U_r$;合成时必须按“已知 $U_{r-1}$ → 求并固定当前维 → 计算已知 $U_r$ → 下一维重新建模”的顺序执行,本文不提供联合多维 ILP。
  • 词典序合法不要求每维严格递增;已经由前维承载的依赖,其后续距离甚至可以为负。
  • Permutable band 要求比普通 lex-positive 更强的非负距离结构;coincident 维则要求所有相关保序依赖在该维距离为零。
  • sequence、fusion、interchange 与 skewing 都必须从完整依赖关系证明合法;stencil 倾斜 $(t,t+i)$ 把三个距离变为 $(1,2),(1,1),(1,0)$。
  • 两个调度可以同样合法却有不同并行性与局部性;合法性定义可行边界,性能目标在边界内另行选择。