简单的并发调度模型

什么时候需要并发?

由于片上buffer 宝贵,且不同程序中的同一个计算操作对存储开销具有动态性,同一个程序中不同计算操作的先后顺序具有动态性,多个计算单元往往共享同一块片上 buffer 资源以便提高 buffer 利用率以及交换上下文数据。多对一便存在仲裁竞争等问题。

Concrrent

最简单的 baseline 是 sequential 模型,对 buffer 的访存操作串行化,某个计算单元运行周期内拥有对 buffer 完整的使用权。如果当计算和访存开销占用完全相等(如图 b),此时 buffer 的 utilization 是 100%,不需要进行额外优化。

解耦并发访存和计算能够提升优化的必要条件是:

  • 硬件资源有余:算子的计算需求和访存需求存在不平衡,比如由于 locality,对高层存储访问时间相对于计算时间总是稀疏的。;
  • 算法资源有余:存在可以并行计算调度的其余算子。

由于图结构依赖以及访存-计算的短板效应,找出理想并发提升上限应该是一个 NP 问题。不过可以通过以下简单分析方法确定粗糙上下限。假设一个算子 \(i\) ,对 buffer 总访存量以及计算需求是 \(X_i=[x_{buf}^i,x_{flops}^i]\),硬件的 buffer 带宽和计算能力是 \(V=[v_{buf},v_{flops}]\) 。则其计算延时为 \(t_i = \max{\frac{X_i}{V}}\),整体计算延时有 \(t_{c} = \sum t_i = \sum \max{(\frac{X_i}{V})}\);而假设并发后能够充分利用 buffer 资源以及计算算力,则其总延时为 \(t_{c} = \max{(\sum \frac{X_i}{V})}\);潜在提升倍数为 \(\frac{t_{s}}{t_{c}}\),实际情况由于后文提到的各类约束,这是一个大概率开区间的上限。

并发执行的建模描述

假设要处理 N 个计算操作,在 sequential 模型上用一个序列即可表征,接下来讨论如何在并发模型上表征计算的某一种执行。

假设在图 a 简单的硬件模型下,假设每个基础调度单元包含一次在时间上连续的计算和一次在时间上连续的访存,如果某种计算输入输出访存存在间隔,应当拆分成两个操作。该基础调度用 5 个参数即可表示,计算起始时间 \(t_{flops}\) ,访存起始时间 \(t_{buf}\),计算持续时间 \(T_{flops}\) ,访存持续时间 \(T_{buf}\),以及该操作的计算类型 \(Op\) (比如属于 Compute1)。

那么我们的调度可用俩个序列表示:

  • 访存序列 \(S_{buf}=\{t_{buf}^1,t_{buf}^2,t_{buf}^3,\cdots,t_{buf}^N\}\)
  • 计算序列 \(S_{flops}=\{t_{flops}^1,t_{flops}^2,t_{flops}^3,\cdots,t_{flops}^N\}\)

通过筛选计算序列的计算类型,可继续导出每种计算类型的计算序列 \(S^1_{flops},S^2_{flops}\)

该调度应当满足以下四个约束:

  • 访存资源不冲突: 对于访存序列有 \(\Delta t_{buf}^i= t_{buf}^{i+1}-t_{buf}^i \ge T_{buf}^i\)
  • 计算资源不冲突: 对于任意类型计算序列,有 \(\Delta t_{flops}^i= t_{flops}^{i+1}-t_{flops}^i \ge T_{flops}^i\)
  • 缓冲区资源有限: 即访存和计算的发生时间不可以相聚太远,对于 buffer 到计算单元的输入型节点 \(t_{flops}^i-t_{buf}^i \in [0, D_{in}]\) ,对于计算单元到 buffer 的输出型节点 \(t_{flops}^i-t_{buf}^i \in [-D_{out}, 0]\)。其中 \(D_{in}\) \(D_{out}\) 是缓冲区深度。
  • 依赖性:每种序列都是计算依赖图的拓扑序列。

并发调度尝试

接下来我们尝试给出一种启发式算法,能够找出某种最优的计算结果。

搜索空间

首先是表达并通过约束限制搜索空间。保证访存资源约束始终满足,只需要保证任意两点之间的间隔大于访存时间。定义:

\[\begin{align} t_{buf}^{i+1}-t_{buf}^i &= T_{buf}^i + \alpha_{buf}^i \\ \alpha_{buf}^i &\ge 0 \end{align} \]

此时 \(t_{buf}^i\) 是对 \(T_{buf}^i+\alpha_{buf}^i\) 在起始点 \(t_{buf}^1\) 的离散积分,对于序列 \(\{T_{buf}^i\}\) 是已知的,我们只需要搜索非负数序列 \(\{\alpha_{buf}^i\}\) 以及非负数 \(t_{buf}^1\) 就可以保证满足访存约束。

我们用访存时间插值表述序列,还需要表述算子的排序,排序可用图通过深度/广度遍历算法生成保证拓扑序。

此时对于“计算资源不冲突”和“缓冲区资源有限”这两类约束只能维持其一,而另一个加入代价函数之中。“计算资源不冲突”的约束表达和“访存资源不冲突”类似,而“缓冲区资源有限”这一约束可以通过搜索计算时间 \(t_{flops}^i-t_{buf}^i\) 并限制其空间实现。直观来看一般多个单元共用访存,冲突主要发生在访存上而非计算单元,因此倾向保持“缓冲区资源有限”约束满足。

损失函数

损失函数肯定包含系统最终完成延时,为了能更快收敛,也可以加入系统资源利用率损失项。

\[L = C \times f(t_{buf}^N+T_{buf}^N) + g(u_{buf},u_{flops1},u_{flops2}) \]

实验结果

待补充...

posted @ 2025-07-18 22:40  DevilXXL  阅读(27)  评论(0)    收藏  举报