2022年全国研究生数学建模竞赛华为杯
D题 PISA架构芯片资源排布问题
原题再现:
一、背景介绍
芯片是电子行业的基础,在当前日益复杂的国际形势下,芯片成了各个大国必争的高科技技术。本课题关注网络通信领域的交换芯片,传统的交换芯片功能固定,当出现新的网络协议时,必须重新设计芯片,而芯片从设计到使用,往往需要几年的时间,因此固定功能的交换芯片大大降低了研发效率,为了解决此问题,诞生了可编程的交换芯片。PISA(Protocol Independent Switch Architecture)是当前主流的可编程交换芯片架构之一,其有着和固定功能交换芯片相当的处理速率,同时兼具了可编程性,在未来网络中具有广阔的应用场景[1-2]。
在对PISA架构作进一步说明之前我们首先澄清几个基本概念:
1. 报文:报文即网络通信中传输的数据包,在网络通信中,用户传输的数据被封装成一个个的数据包进行传输。
2. 基本块:基本块是源程序的一个程序片段,对源程序进行基本块划分会将源程序划分为一个个的基本块。至于基本块如何划分本身也是一个值得探讨的问题,但超出了本问题的范围,在此不再多加赘述。
3. 流水线:流水线为一系列处理单元串联构成,报文在流水线中按次序依次通过每个处理单元,最终完成处理。流水线各级即是指流水线中各处理单元。
PISA架构如图1所示,其包括报文解析(parser)、多级的报文处理流水线(Pipeline Pocket Process)、以及报文重组(Deparser)三个组成部分。报文解析用于识别报文种类;多级的报文处理流水线用于修改报文数据,在实际的PISA架构芯片中,不同的芯片流水线的级数可能不同;报文重组用于报文重新组装。本课题只关注其中多级的报文处理流水线部分。在PISA架构编程模型中,用户使用P4语言描述报文处理行为得到P4程序,再由编译器编译P4程序,进而生成芯片上可以执行的机器码。编译器在编译P4程序时,会首先将P4程序划分为一系列的基本块,再将各基本块排布到流水线各级当中。由于各基本块均会占用一定的芯片资源,将基本块排布到流水线各级即是将各基本块的资源排布到流水线各级当中(即需要确定每个基本块排布到流水线哪一级),因此我们将基本块的排布问题称作PISA架构芯片资源排布问题。在实际的PISA架构芯片的设计中,为了减少连线的复杂度,往往对流水线各级的资源、以及流水线各级之间的资源有着多种多样的约束条件,这一系列复杂的资源约束条件使得资源排布问题尤为困难。然而,芯片的各类资源均有限,越高的资源利用率意味着能够越好的发挥芯片的能力,让芯片支持更多的业务,因此,高资源利用率的资源排布算法对于编译器设计至关重要。
三、输入数据说明
输入数据包含三个附件,分别给出了各基本块资源使用情况、各基本块读写的变量信息、以及各基本块在流图中的邻接基本块,各附件格式如下:
(1)attachment1.csv:各基本块使用的资源信息
表中第一列为基本块编号,第2列到第5列为各基本块使用的四种资源的数目,资源总共分为四种(TCAM、HASH、ALU、QUALIFY),比如由上表可以知道,0号基本块需要占用2个ALU资源,4号基本块需要占用10个ALU资源和3个Qualify资源。
(2)attachment2.csv:各基本块读写的变量信息
表中第一列表示基本块编号,第二列中“W”表示写,“R”表示读,后续列表示本基本块写或者读的变量,当某一行从第三列及后续列没有任何元素时,说明此编号的基本块没有写(或读)任何变量(此时该基本块单纯作为连接其它基本块的中间基本块,没有执行任何计算)。例如:0号基本块写了变量X0、X1,但没有读任何变量,1号基本块既没有写任何变量,也没有读任何变量。
(3)attachment3.csv:各基本块在流程图中的邻接基本块信息
表中第一列为基本块编号,后续列为与当前基本块在流程图中邻接的基本块编号,即在流程图(如图2左图所示)中,本基本块到后续列中的基本块之间存在有向边连接。比如,由上表第一行可知,0号基本块到1号基本块和2号基本块之间存在有向边连接(即0号基本块执行结束后可以跳转到1号基本块或2号基本块执行),由第三行可知从2号基本块出发没有边(即基本块2执行后程序结束,不会再跳转到其它基本块执行)。通过此文件可以确定基本块在源程序的执行顺序,确定每个基本块执行后跳转的目的基本块,进而构建起基本块的流程图。
四、问题
本课题需要建立资源排布问题的数学模型,并在此基础上处理如下两个问题。
问题1:给定资源约束条件如下:
(1)流水线每级的TCAM资源最大为1;
(2)流水线每级的HASH资源最大为2;
(3)流水线每级的ALU资源最大为56;
(4)流水线每级的QUALIFY资源最大为64;
(5)约定流水线第0级与第16级,第1级与第17级,…,第15级与第31级为折叠级数,折叠的两级TCAM资源加起来最大为1,HASH资源加起来最大为3。注:如果需要的流水线级数超过32级,则从第32开始的级数不考虑折叠资源限制;
(6)有TCAM资源的偶数级数量不超过5;
(7)每个基本块只能排布到一级。
在上述资源约束条件下进行资源排布,并以占用的流水线级数尽量短为优化目标。请给出资源排布算法,输出基本块排布结果,输出的结果格式如下:
问题2:考虑如图3所示的流图,基本块2和基本块3不在一条执行流程上(因为基本块1执行完后要么执行基本块2,要么执行基本块3,基本块2和基本块3不可能都执行)。准确来说,在P4程序流程图中,由一个基本块出发可以到达另一个基本块则两基本块在一条执行流程上,反之不在一条执行流程上。对于这种不在一条执行流程上的基本块,可以共享HASH资源和ALU资源,基本块2和3中任意一个的HASH资源与ALU资源均不超过每级资源限制,基本块2和3即可排布到同一级。据此,对问题1中的约束条件(2)、(3)、(5)作如下更改:
(2)流水线每级中同一条执行流程上的基本块的HASH资源之和最大为2;
(3)流水线每级中同一条执行流程上的基本块的ALU资源之和最大为56;
(5)折叠的两级,对于TCAM资源约束不变,对于HASH资源,每级分别计算同一条执行流程上的基本块占用的HASH资源,再将两级的计算结果相加,结果不超过3。
其它约束条件同问题1,更改资源约束条件后重新考虑问题1,给出排布算法,输出基本块排布结果。输出格式同问题1。
整体求解过程概述(摘要)
随着全球“芯”缺浪潮的持续发酵,作为“工业粮食”的芯片技术成为我国亟待突围的产业之一。PISA 作为兼具良好处理速度与可编程性的交换芯片架构,有效缓解了传统固定功能的交换芯片研发效率低下的问题。为充分发挥芯片能力,高资源利用率的芯片资源排布算法的选择显得尤其重要。鉴于此,本文将复杂的基本块资源排布问题拆解为多个子问题,并结合各类约束条件,通过建立目标规划模型与基本块排布模型逐步求解,然后设计启发式规则对求解结果进行优化,最终将得到的尽可能短的流水线级数作为问题的解。
针对问题一,本文将其划分为依赖关系分析和基本块资源排布两个子问题。针对子问题一,分析处理附件 attachment3.csv 数据得到控制流图 CFG 与控制流图的前向支配树FDT,CFG 和 FDT 差集即为基本块的控制依赖关系。其次,读取附件 attachment2.csv 中基本块的读写关系,利用广度优先搜索算法 BFS 判断基本块间的连通性来确定数据依赖关系。将控制依赖与数据依赖关系取并集得到完整的依赖关系。针对子问题二,将完成的依赖关系抽象为带权有向无环图,初步确定基本块在流水线中排布的层级关系。然后,以占用尽量短的流水线级数为主要目标,结合 attachment1.csv 中的基本块资源需求和给定的资源约束条件建立了目标规划模型。模型的求解设计了基本块排布模型,依据串行排布的原则,从无前序依赖关系的基本块中依据随机规则选取基本块放入最早可放入的流水线级,直到所有基本块完成排布。问题一得出流水线占用级数为 40 级。
针对问题二,由于不在一条执行流程上的基本块可以共享 HASH 资源和 ALU 资源,我们在问题一的基础上引入不在一条执行流程上的基本块的概念,缓解了问题一中对HASH 和 ALU 的占用问题。本文将问题二拆解为判断两个基本块是否在同一条执行流程、基本块资源排布两个子问题。针对子问题一,根据程序流程图,利用广度优先搜索算法判断两个基本块是否在一条执行流程上,即从一个基本块出发是否可到达另一个基本块,获得基本块执行流程关系对称矩阵。针对问题二,同样以占用尽量短的流水线级数为主要目标,结合不在同一执行流程上的基本块可共享 HASH 和 ALU 资源这一条件与更改后的三个约束,建立与问题一类似的目标规划模型。求解时对于有新的基本块排布的流水线,合并该级流水线中与其处于不同控制流的基本块对 HASH 和 ALU 的资源需求。问题二得出流水线占用的级数为 34 级。
求解方案优化考虑到随机规则选取的不稳定性,对基本块选取规则进行优化,本文构建了最早开始时间 EST、最多紧后资源块 MST、最早开始时间且最多紧后资源块EST_MST 等 6 种启发式规则,依据启发式规则选取要加入流水线的基本块。本文综合了10 次随机规则和 6 种启发式规则求解的最优结果作为最终的流水线排布方案。优化后,问题一得出流水线占用级数为 40 级。问题二得出流水线占用的级数为 34 级。最后,本文发现了问题一的性能瓶颈在于 HASH 资源,问题二的性能瓶颈在于TCAM 资源。通过综合考虑控制依赖、数据依赖和各级流水线的资源约束条件,证明了所提方案的有效性与普适性。总结了所提算法与模型的优缺点,并对未来研究工作进行了展望。
模型假设:
[1] 假设实验场景均位于理想状态,不受温度和电磁等因素影响。
[2] 假设实验过程中不存在由芯片故障导致的误差。
[3] 假设只关注 PISA 架构芯片多级的报文处理流水线部分。
[4] 假设每级流水线除题目所给的资源约束外,其他条件均相同。
[5] 假设可排布的流水线级数没有上限。
[6] 假设随机模型种取随机数的过程是完全随机的。
问题分析:
问题一分析:
问题一要求考虑各基本块间的数据依赖、控制依赖关系情况下,结合给定的资源约束条件,实现基本块资源排布并优化,使占用的流水线级数尽量短,以求最大化芯片资源利用率。题目给出 attachment1.csv、attachment2.csv、attachment3.csv 三个附件分别为各基本块使用的资源信息、各基本块读写的变量信息以及各基本块在流程图种的邻接基本
块信息。
通过对题目分析,我们得出本题可拆解为以下三个子问题:
子问题一:通过设计算法与模型,对附件二、附件三中的数据进行处理分析,确定各基本块间的控制依赖关系与数据依赖关系,从而初步确定基本块在流水线中排布的层级关系。
子问题二:结合题目所给资源约束条件与附件一的资源使用数据,建立模型,确定基本块在流水线中的级数排布情况。
子问题三:对基本块排布进行优化,使得基本块占用的流水线级数尽可能短。
问题二分析:
问题二在问题一的基础上引入不在一条执行流程上的基本块的概念,对于不在一条执行流程上的基本块,可以共享 HASH 资源和 ALU 资源。由此,问题二对于问题一缓解了 HASH 和 ALU 的占用。问题同样要求给出基本块在流水线中的排布算法与结果,但对约束条件进行了更改。
结合分析,问题二可拆解为以下两个子问题:
子问题一:根据程序流程图,建立模型,确定一个基本块出发是否可到达另一个基本块,即两个基本块是否在一条执行流程上。
子问题二:结合不在同一执行流程上的基本块可共享 HASH 和 ALU 资源这一条件与更改后的约束,建立模型,确定基本块在流水线中的级数排布情况,并使占用的流水线级数尽量短。
模型的建立与求解整体论文缩略图
全部论文请见下方“ 只会建模 QQ名片” 点击QQ名片即可
程序代码:
部分Python程序如下:
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
import csv
import scipy as sp
# 入度
inGF = []
# 第一步:dfs
dfn = []
rak = []
fa = []
search_path = []
# 第二步:sdom
val = []
bel = []
sdom = []
# 第三步:idom
idom = []
# 比较获取 CDG
cdg = []
idomGF = nx.DiGraph()
def read_csv3(file_name):
f = open(file_name, encoding='UTF8')
csv_reader = csv.reader(f)
control_edges = []
for line in csv_reader:
adj_num = len(line)
if adj_num > 1:
for i in range(1, adj_num):
control_edges.append((int(line[0]), int(line[i])))
f.close()
# print(control_edges)
return control_edges
def subgraph(pointList, linkList):
G = nx.DiGraph()
GF = nx.DiGraph()
# 转化为图结构
for node in pointList:
G.add_node(node)
GF.add_node(node)
for link in linkList:
G.add_edge(link[0], link[1])
GF.add_edge(link[1], link[0])
return G, GF
def dfs(GF):
# GF 的 root 为人为添加的序号最大的根
root = len(GF.nodes) - 1
T = nx.dfs_tree(GF, root)
for n in GF.nodes():
fa.append(0)
dfn.append(n)
global rak
rak = list(T) # 所有节点
for i in range(0, len(fa)):
dfn[rak[i]] = i
for i in list(T.edges): # 所有边
fa[i[1]] = i[0]
# print(dfn)
# print(rak)
# print(fa)
def find(v):
# 还未被遍历
if v == bel[v]:
return v
tmp = find(bel[v])
if (dfn[sdom[val[bel[v]]]] < dfn[sdom[val[v]]]):
val[v] = val[bel[v]]
bel[v] = tmp
return bel[v]
def tarjanFDT():
# 初始化 val 和 sdom
for i in range(0, len(dfn)):
val.append(i)
sdom.append(i)
bel.append(i)
idom.append(i)
# 从大到小遍历 dfs 树
for i in range(len(dfn) - 1, 0, -1):
# dfs 序最大的点 u
u = rak[i]
# 获取 GF 原图中所有 u 的前驱
neighbors = G.neighbors(u)
for v in neighbors:
find(v)
if (dfn[sdom[val[v]]] < dfn[sdom[u]]):
sdom[u] = sdom[val[v]]
# print(sdom)
sdomGF = nx.DiGraph()
for i in range(0, len(sdom)):
sdomGF.add_node(i)
sdomGF.add_edge(sdom[i], i)
bel[u] = fa[u]
u = fa[u]
neighbors = sdomGF.neighbors(u)
for v in neighbors:
find(v)
if sdom[val[v]] == u:
idom[v] = u
else:
idom[v] = val[v]
for i in range(0, len(dfn)):
u = rak[i]
if idom[u] != sdom[u]:
idom[u] = idom[idom[u]]
global idomGF
for i in range(0, len(idom)):
idomGF.add_node(i)
idomGF.add_edge(idom[i], i)
# nx.draw_networkx(sdomGF, with_labels=True)
# plt.show()
# nx.draw_networkx(idomGF, with_labels=True)
# plt.show()
def getInGF():
# 遍历 GF 所有点
for i in range(0, len(GF.nodes)):
# 初始化:0 标识入度为 0
inGF.append(0)
for edge in GF.edges:
inGF[edge[1]] = 1
def addGFRoot():
# print(len(GF.nodes))
GF.add_node(len(GF.nodes))
for i in range(0, len(inGF)):
if inGF[i] == 0:
GF.add_edge(len(GF.nodes) - 1, i
# nx.draw_networkx(GF, with_labels=True)
# plt.show()
if __name__ == '__main__':
# 读 attachment3.cvs
linkList = read_csv3("./data/attachment3.csv")
pointList = []
for i in range(0, 607):
pointList.append(i)
# 原始有向无环图 G,反向图 GF
G, GF = subgraph(pointList, linkList)
# 获取 GF 入度为 0 的所有点
getInGF()
# 为 GF 添加根节点
addGFRoot()
# 获取 G 的前向支配树,也就是 GF 的支配树,存储在 idomGF 中(即
FDT)
dfs(GF)
tarjanFDT()
# 对比 G 原图和 FDT 图,寻找在 G 但不在 FDT 中的边,得到 CDG
for edgeG in G.edges:
edgeGf = (edgeG[1], edgeG[0])
# 标识是都存在相同,初始化为 0
flag = 0
for edgefdt in idomGF.edges:
if edgeGf == edgefdt:
flag = 1
break
else:
continue
if flag == 0:
cdg.append(edgeG)
f = open("./data/control_dependance_less_equal.txt", "w")
f.writelines(str(cdg))
f.close()
print(len(cdg))
print(cdg)