一、概述
线性流水线与非线性流水线是CPU中指令处理流水线的一种分类标准。线性流水线很好理解,就是一条路走到黑的流水线;非线性流水线则不同,它可能存在前馈与反馈,每个部件可能使用一次或多次,它就没法像线性流水线那么一个一个部件按部就班的走。因此出现了一个问题,如果我第一个任务第二次使用部件A,第二个任务恰好第一次也使用部件A,这会怎么样?
出现矛盾了,流水线卡住了。这不好,因此需要流水线调度算法来安排好每一个任务,在让它们不冲突的同时,最大可能提高流水线的效率。
二、分析
1、非线性流水线的描述
线性流水线能够用流水线连接图唯一表示,非线性流水线则不行,它的描述需要流水线连接图和预约表的协同作用。一个流水线连接图可能有好几种执行顺序,也就对应好几个可能的预约表,一个预约表也可能对应好几张流水线连接图。只有同时看这两者,才能确定最终的处理顺序。下图就是一张预约表。

这是一个有四个组件,七个流水段的流水线。横轴代表时间,纵轴代表组件。×则代表在某一时刻当前使用的组件。因此我们可以得知,每个框中只能有一个×,否则会出现冲突。
2、调度算法的推导
首先,我们要提出三个定义:
启动距离、禁止向量、冲突向量。
启动距离指的是第一个任务进入流水线后,第二个任务进入时,不发生冲突的时间。显然,启动距离又长又短,我们的目的就是找出平均启动距离最短的一个任务载入序列。
禁止向量指的是预约表中每一行任意两个×之间距离的集合。例如上图中,禁止向量为(2,4,6)。
冲突向量的指的是如下一个向量:(Cm,Cm-1......C2C1),其中C为0或1;m为禁止向量中的最大值。对于Ci,如果禁止向量在i中,则Ci=1,否则Ci=0。
上图预约表对应的冲突向量指的是(101010)。
我们可以这样理解冲突向量:对于一个任务x,其耗时为nk,n为流水段数量,k为每一流水段耗费的时间。假设各流水段好费时间相同。那么,在x进入后的第k,2k...nk时刻,均可以加载下一个任务y。然而可能出现冲突。可以通过计算冲突向量来规避这种冲突:
若冲突向量的Cm=1,则x进入后的第mk时刻不能加载y,否则会冲突。如图,C1=0,C2=1。如果在x进入后2时刻,即横轴为3的时候加载y,那么在横轴为5的时候,x与y争用S3部件,出现冲突。也就是说,y可以在C1、C3、C5也就是横轴为2、4、6的时候载入。
现在我们假设y在C3载入,也就是在横轴为4处载入。对于y来说,在它没结束之前,有哪些时刻不能载入任务呢?很容易想的一点是:在C2、C4、C6不能载入。因为y是和x一样的任务,因此x中不能载入的时刻,y也不能载入。但这够了么?还不够,x还会对y之后的任务产生影响,如何表达这种影响呢?用x的冲突向量来表达。
接着拿y在C3载入举例,这时,从x的冲突向量可以知道,横轴为3、5、7的时候不能载入,从y的冲突向量可以知道,横轴为6、8、10的时候无法载入。做一下并集,在3、5、6、7、8、10的时候不能载入。由于y在4的时候载入,因此仅需要注意5、6、7、8、10即可。由5、6、7、8、10可以得出y的冲突向量101111。这是我们手动算出来的。如何通过数学方法计算呢?
通过右移与按位取并操作进行。

如上图,右移3代表选择x的非冲突时刻C3,右移3得到的000101代表对于y来说,x的影响,101010是y自身的影响。二者叠加可得所有不能载入的时刻。

再如上图,初始任务x的冲突向量为1010100,第二个任务y若选择在2时间后载入,那么x对其的影响可以写为0010101;两个影响叠加为1010101,与最初的影响相同;第二个任务y若选择在1时间后载入,那么x对其的影响为0101010,两个影响合并为1111110,这是y的冲突向量;第三个任务z若选择在y后1时间载入,那么y对其的影响为0111111,两个影响合并为1111111。
也可以将1111111看成是xyz三个任务共同影响的结果,因为1111111是x的冲突向量右移两位并上y右移一位并上z得到的。
如此,我们需要得到所有冲突向量右移所有的0的位置得到的所有的结果,然后按位取并,继续右移继续取并,直到结果与之前的相同。这样我们就会得到一张图。

上图是101010所得到的的图。这其中的所有循环,就是我们要找的非冲突的载入序列。也就是我们算法最后需要的结果。
三、代码实现
由1、2可以得知,算法的整体思路可以按如下几步:
第一,输入预约表;
第二,得到初始冲突向量;
第三,生成状态图;
第四,找到所有循环。
其中,第三和第四步可以转化为如下问题:生成一张有向图,记录其中所有循环;但是我的算法可以按另一种形式描述:生成一棵树,当根节点对应的叶子节点与之前的节点相同是,记录之前的节点到根节点的路径。
以下是我的代码。
1、输入函数
为便于输入,选择从外部文件读取预约表,函数如下:
int readTable(int a,int b)
{
FILE*fp = NULL;//需要注意
fp = fopen(F_PATH, "r");
if (NULL == fp) return -1;//要返回错误代码
for (int i = 0; i<a; i++)
for (int j = 0; j < b; j++)
{
int tmp;
fscanf(fp,"%d", &tmp);
ResTable[i][j] = tmp;
}
fclose(fp);
fp = NULL;//需要指向空,否则会指向原打开文件地址
return 0;
}
使用fopen读入文件流,用fp储存文件流,然后生成预约表对应的二维数组即可。
2、得到初始冲突向量
为得到冲突向量,首先要得到禁止向量。由于禁止向量中没有重复元素,选择使用set保存。然后遍历各行,计算出预约表中不为零的元素对应的列坐标的差的绝对值,保存在dis中。
然后遍历dis,使用string生成冲突向量,dis中的值作为string中元素下标,这些下标对应的值为1,其余为0。从而生成初始冲突向量。
void Create_Initial_Conflict_Vector(int a,int b)
{
set<int> dis;//不为零之间的距离
for (int i = 0; i<a; i++)
{
vector<int> tmp;
for (int j = 0; j < b; j++)
{
if (ResTable[i][j] !=

最低0.47元/天 解锁文章
4610





