- ILP(整数线性编程):除了目标函数和约束都是线性组合之外,变量的取值必须是整数
- TCS——Time-constrained scheduling(时间约束调度)
- RCS——Resource-constrained scheduling(资源约束调度)
- ASAP——as soon as possible (尽早调度)
- ALAP——as last as possible(最迟调度)
先来一张图,左边ASAP,右边ALAP
对于TCS与RCS的区别,可以从两点去理解
1.起始条件不同:RCS,顾名思义,就是在有资源约束的情况下,比如我的乘法器只有3个,ALU有5个。TCS,给定一个最大的延迟,即调度完成不能超过这个时间。
2.调度目标不同:RCS,通过调整具有机动性的节点(可以调整时间步执行的节点),最大能力减少执行时间。TCS,减少同一个时间步中使用的资源数量。
现在要对一个TCS问题进行ILP建模。0,1规划
- 目标函数:最小化同一时间段不同资源的使用数量,这里就简化为一种资源PE
- 决策变量x[i][j],为1表示第j个算子在第i个时间步执行
- 约束条件:
1.依赖约束:保证依赖的算子的先后顺序
2.资源约束:TEC 中同一层的算子数量不超过 PE 的数量
3.起步时间唯一性约束
import numpy as np
import pulp as pl
def vertex_rec(vertex_num, leaf):
"""
记录DFG中节点信息
:param vertex_num:
:param leaf:
:return:
"""
global end_time
if leaf: # 记录子节点
print('依次输入子节点,边类型(以逗号分割):')
else:
print('依次输入节点编号,开始时间,结束时间,节点类型,有无父节点(以逗号分割):')
for i in range(1, vertex_num+1):
input_str = input(str)
tmp_list = [int(num) for num in input_str.split(',')]
if leaf:
leaf_vertex[i] = tmp_list
else:
vertex_time[i] = tmp_list
if end_time < vertex_time[i][2]:
end_time = vertex_time[i][2]
if __name__ == '__main__':
max_src = 0
tm_II = int(input('输入启动间隔:')) # 启动时间间隔
end_time = 0
vertex_num = int(input('输入节点个数:')) #节点个数
vertex_time = [[] for i in range(0, vertex_num+1)] # 记录节点编号、开始时间步、结束时间步、节点类型、有无父节点
leaf_vertex = [[] for i in range(0, vertex_num+1)] # 记录子节点以及边类型
# x[i][j]为1则表示节点i可以在时间j开始执行
x = np.zeros([vertex_num+1, end_time+1], dtype=int)
vertex_rec(vertex_num, 0)
vertex_rec(vertex_num, 1)
print("end_time", end_time)
print("vertex_time(开始时间步、结束时间步、节点类型、有无父节点):")
for i in range(vertex_num + 1):
print("vertex_time[{}]:".format(i), vertex_time[i])
print("leaf_vertex(子节点以及边类型):")
for i in range(vertex_num + 1):
print("leaf_vertex[{}]:".format(i), leaf_vertex[i])
# start to module by pulp
# variable
cols = [int(c) for c in range(1, vertex_num+1)]
rows = [int(r) for r in range(0, end_time+1)]
PE = pl.LpVariable("PE", 0, 100, pl.LpInteger) # 这个地方太重要了
# build x[i][j] --> 第j个算子在第i个时间步开始进行计算
x = pl.LpVariable.dicts("x", (rows, cols), lowBound=0, upBound=1, cat=pl.LpInteger)
prob = pl.LpProblem("Minimize resources", pl.LpMinimize)
# 目标函数
prob += PE
# resource_per_clock = pl.lpSum([[x[0][c]] for c in cols])
# for r in range(1, end_time+1):
# if resource_per_clock <= pl.lpSum([[x[r][c]] for c in cols]):
# resource_per_clock = pl.lpSum([[x[r][c]] for c in cols])
# print("已经有了:", resource_per_clock)
# prob += pl.lpSum([resource_per_clock])
prob += PE
# 起步时间唯一性约束
for c in cols:
prob += pl.lpSum([x[r][c] for r in rows]) == 1
# 资源约束
for r in rows:
prob += pl.lpSum([x[r][c] for c in cols]) <= PE
# 依赖约束
# 对于所有存在子节点的节点,给出约束
for c in cols:
offset = 0
for vertex in range(len(leaf_vertex[c]) // 2): # 对每一个依赖
if leaf_vertex[c][offset] != 0:
son = int(leaf_vertex[c][offset]) # 子节点
vertex = c # 此节点
# 此节点开始时间步vertex_time[c][1], 结束时间步vertex_time[c][2]
vertex_tm = [int(i) for i in range(vertex_time[c][1], vertex_time[c][2]+1)] # vertex_time[c][2]没有加1,被搞了
# 子节点开始时间步vertex_time[son][1], 结束时间步vertex_time[son][2]
son_tm = [int(i) for i in range(vertex_time[son][1], vertex_time[son][2]+1)]
# s_start = v_start = 0 # v_start-->此节点的启动时间
# for s_t in son_tm:
# s_start = s_t*x[s_t][son]
# for v_t in vertex_tm:
# v_start = v_t*x[v_t][vertex]
# s_start = sum([s_t*x[s_t][son] for s_t in son_tm]) # s_start-->子节点的开始时间
# v_start = sum([v_t * x[v_t][vertex] for v_t in vertex_tm])
# print("{}的子节点, {}开始时间{},{}子节点结束时间{}".format(vertex, vertex, v_start, son, s_start))
# prob += (s_start - v_start) <= -100
# s_start = sum([s_t * x[s_t][son] for s_t in son_tm]) # s_start-->子节点的开始时间
# v_start = sum([v_t * x[v_t][vertex] for v_t in vertex_tm])
# prob += s_start - v_start >= 1
print("{}的子节点是{}, {}开始时间{},子节点{}结束时间是{}".format(vertex, son, vertex, v_start, son, s_start))
prob += pl.lpSum([s_t * x[s_t][son] for s_t in son_tm]) - pl.lpSum([v_t * x[v_t][vertex] for v_t in vertex_tm]) >= 1
offset += 2
# for c in cols:
# if vertex_time[c][1] == vertex_time[c][2]:
# print("时间相同节点:",c)
# prob += x[vertex_time[c][1]][c] >= 1
prob.solve()
print("Status:", pl.LpStatus[prob.status])
for v in prob.variables():
print(v.name, "=", v.varValue)
print("Number of resources", prob.objective.value())
# print(PE)
首先说明一下,这是高级硬件的一个研究课题,建模使用的是python中的pulp,pulp是现学的,所以在做的时候遇到了很多困难,也许当自己把代码写出来了在来看这个代码的时候,觉得这个很简单,核心代码就那几个。但就是自己是现学的pulp,而且python也不太熟练,所以导致这个代码写了好长时间。最终的运行效果是这样的
*输入数据
# 5 个节点,均无机动性
5
5
1,0,0,0,0
2,1,1,0,1
3,1,1,0,1
4,1,1,0,1
5,1,1,0,1
2,0,3,0,4,0,5,0
0,0
0,0
0,0
0,0
输出截图
和预想的一样。
过程记录
- 对于依赖约束中的
prob += pl.lpSum([s_t * x[s_t][son] for s_t in son_tm]) - pl.lpSum([v_t * x[v_t][vertex] for v_t in vertex_tm]) >= 1
,开始一直不知道该怎么用pulp来描述这个约束(注释里面的那个刚开始不行,当我刚试了下好像又行,wdnmd) - 目标函数的给出,这个真的搞我,这个就是要让最小化所有行中和最大的那个。大概经历了三个阶段,第一个阶段,向对应注释的那样,
resource_per_clock
,但是后面当我打印这个值的时候,就会发现这个就是最后一行对应的数据。第二个阶段,各种搜索,查看文档,这种搜索不行的话就换关键字搜索,给我整晕了。第三个阶段,和老师交流了一下,一下子茅塞顿开。直接用目标函数去表示最小化所有行中和最大的那个是表示不出来的,所以这里就有这个prob += pl.lpSum([x[r][c] for c in cols]) <= PE
约束。当我们把PE也作为一个决策变量的时候,这个时候奇妙的事情就发生了。目标函数就是prob += PE
,这种感觉真的美妙。
如果发现程序的输出让你感到不可思议的时候,那就说明你的变量输出还不够!