1. 实验简介

使用Java编写NoC(Network-on-chip)片上网络模拟器,用于模拟消息的路由过程。

  • 网络结构:2D Mesh,规模8*8
  • 流量模式:uniform,transpose,hotspot
  • 路由算法:主要Negative-first for 2D Meshes,并与XY路由算法、West-first算法进行对比。

2. 算法描述

2.1 Mesh网络

二维网格(2D-Mesh)拓扑结构,如图所示,该拓扑结构的特点是:每个节点的 上、下、左、右近邻相连,该拓扑具备结构简单、可扩展性较好、便于分析等优点。目前,2D-Mesh 是片上网络上最常用的一种拓扑结构。

本实验基于8*8的2D Mesh网络结构下进行。

2.2 Turn Model模型

Turn Model模型是把每个路由节点相邻的4个通道分别用E(东)、W(西)、S(南)、N(北)四个方向标识,规定Turn Model转向指的是垂直角度的转向,例如自西向南的转向用WS表示,自北向东用NE表示,以此类推,因此所有的转向方式有:WE、EW、SW、WS、EN、NE、ES、SE共8种转向,如图所示。而这8种转向可以构成如下两种环。

2.3 流量模式

在片上网络(Network-on-Chip, NoC)的研究和模拟中,流量模式是指数据包在网络中的产生、传输和目的地的方式。不同的流量模式会影响网络中的通信需求和资源利用率,从而对网络的性能产生重要影响。
本实验主要涉及Uniform、Transpose、Hotspot三种流量模式,其含义如下:

  • Uniform(均匀流量模式)
    • 在这种模式下,网络中的每个节点以相同的概率向其他任何节点发送数据包。
    • 这种模式通常用于评估网络在均衡负载下的性能。
  • Transpose(转置流量模式)
    • 转置流量模式通常指的是一种特定的通信模式,其中数据包的源节点和目的节点坐标在某种方式上呈现镜像关系。通信量集中在节点对之间的特定路径上。
    • 例如,在二维网格NoC中,如果一个数据包从节点(0,0)发送到节点(1,1),则另一个数据包可能从节点(1,1)发送到节点(0,0)。
    • 这种模式可以用来模拟某些应用中存在的特定通信模式,如矩阵转置操作。
  • Hotspot(热点流量模式)
    • 热点流量模式是指网络中有一个或几个节点充当热点,这些节点接收或发送大量的数据包,而其他节点则相对较少。
    • 这种模式通常用于模拟那些通信不均匀的应用,例如某些计算任务中的中心服务器或共享资源。
    • 热点流量模式会对网络中的某些部分造成较高的负载,可能会导致拥塞和性能瓶颈。

2.4 Negative-first算法

Negative-First负向优先算法是转弯模型算法中的一种,此外还有West-First西向优先,North-Last北向最后转弯算法。负向优先转弯模型,是禁止数据包在路由的过程中朝负向转弯。其转弯模型图如下:

我们按如下图定义各方向的正负向,在坐标系中正方向为是Y+(北),X+(东),负方向为X-(西方向),Y-(南方向)。

算法通过禁止了一些转弯,避免了死锁环的产生, 该算法中不允许在正向路由的过程中转向负向路由,比如:一个数据包在向北(正向)传输过程中,禁止向西(负向)转向。在向东(正向)传输的过程中,禁止向南(负向)转向。负向优先路由算法规定,任何一个数据包在路由的时候,首先考虑的是向西和南进行自适应路由,然后再向东和北进行自适应路由。

假设当前节点的坐标Current_Node的坐标为(curx,cury),目的节点 Des_Node 坐标为(dstx,dsty)△X=dstx-curx,△Y=dsty-cury。数据包下一跳的方向为dir

  1. △X=0 && △Y=0:当前节点即目的地,已经到达。
  2. △X>0 && △Y=0:目标节点在当前节点X+方向,数据包沿着 X+方向进行发送,如果在发送的过程中,遇到了故障链路或者缓冲已满,则沿着 Y-方向进行一个跳步
  3. △X<0 && △Y=0:目标节点在当前节点X-方向,数据包沿着X-方向进行发送,如果在发送的过程中,遇到了故障链路或者缓冲已满,则沿着Y-方向进行一个跳步
  4. △X=0 && △Y>0:目标节点在当前节点Y+方向,数据包沿着Y+方向进行发送,如果在发送的过程中,遇到了故障链路或者缓冲已满,则沿着X-方向进行一个跳步
  5. △X=0 && △Y<0:目标节点在当前节点Y-方向,数据包沿着Y-方向进行发送,如果在发送的过程中,遇到了故障链路或者缓冲已满,则沿着X-方向进行一个跳步
  6. △X>0 && △Y>0:目标节点在当前节点东北方,数据包沿着X+/Y+方向进行发送,如果在发送的过程中,遇到了故障链路或者缓冲已满,则沿着X-/Y-方向进行一个跳步。这里规定优先按X方向进行。
  7. △X>0 && △Y<0:目标节点在当前节点东南方,数据包沿着Y-方向进行发送,如果在发送的过程中,遇到了故障链路或者缓冲已满,则沿着X-方向进行一个跳步。这里规定优先按Y方向进行。
  8. △X<0 && △Y>0:目标节点在当前节点西北方,数据包沿着X-方向进行发送,如果在发送的过程中,遇到了故障链路或者缓冲已满,则沿着Y-方向进行一个跳步。这里规定优先按X方向进行。
  9. △X<0 && △Y<0:目标节点在当前节点西南方,数据包沿着X-/Y-方向进行发送,如果在发送的过程中,遇到了故障链路或者缓冲已满,则沿着另一方向的负方向进行一个跳步。这里规定优先按X方向进行。

3. 算法实现

3.1 路由相关类

  • IRouting:定义了顶层接口,提供forward方法,接收message类型的参数,用于消息传播。
  • AbstractRouting:抽象父类,包含了一些公有属性和方法。
  • NegativeFirstRoutingXYRoutingWestFirstRouting:路由子类,实现了不同的路由算法。在本实验中,主要分析NegativeFirst算法,另外两个主要用于结果对比。

具体路由选择代码如下:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
private NodeInfo noWrapLinkrt(Q2DMeshNode cur,Q2DMeshNode dst){  
// 当前节点X、Y坐标
int curx = cur.x;
int cury = cur.y;
// 目标节点X、Y坐标
int dstx = dst.x;
int dsty = dst.y;

// 目标与当前节点的相对距离deltaX,deltaY
int xdis = dstx - curx;
int ydis = dsty - cury;

int bufferslc;

// var1表示X上方向,var1表示Y上方向
DirectionEnum var1=DirectionEnum.NONE,var2=DirectionEnum.NONE;
int vchx=0,vchy=0;

XYEnum perfer = XYEnum.O;//1表示优先走x,2表示优先走y

// 当前节点即目标节点
if(xdis==0 && ydis==0){
var1 = DirectionEnum.NONE;
var2 = DirectionEnum.NONE;
perfer = XYEnum.O;
}
// 目标节点在当前节点X轴正方向
else if (xdis>0 && ydis==0){
var1 = DirectionEnum.POSITIVE;//优先x
var2 = DirectionEnum.NEGATIVE;
perfer = XYEnum.X;
}
// 目标节点在当前节点X轴负方向
else if (xdis<0 && ydis==0){
var1 = DirectionEnum.NEGATIVE;//优先x
var2 = DirectionEnum.NEGATIVE;
perfer = XYEnum.X;
}
// 目标节点在当前节点Y轴正方向
else if (xdis==0 && ydis>0){
var1 = DirectionEnum.NEGATIVE;
var2 = DirectionEnum.POSITIVE;//优先y
perfer = XYEnum.Y;
}
// 目标节点在当前节点Y轴负方向
else if (xdis==0 && ydis<0){
var1 = DirectionEnum.NEGATIVE;
var2 = DirectionEnum.NEGATIVE;//优先y
perfer = XYEnum.Y;
}
// 目标节点在当前节点东北方
else if (xdis>0 && ydis>0){
var1 = DirectionEnum.POSITIVE;// 优先x
var2 = DirectionEnum.POSITIVE;
perfer = XYEnum.Y;
}
// 目标节点在当前节点西南方
else if (xdis<0 && ydis<0){
var1 = DirectionEnum.NEGATIVE;// 优先x
var2 = DirectionEnum.NEGATIVE;
perfer = XYEnum.X;
}
// 目标节点在当前节点东南方
else if (xdis>0 && ydis<0){
var1 = DirectionEnum.NEGATIVE;
var2 = DirectionEnum.NEGATIVE;//优先y
perfer = XYEnum.Y;
}
// 目标节点在当前节点西北方
else if (xdis<0 && ydis>0){
var1 = DirectionEnum.NEGATIVE;//优先x
var2 = DirectionEnum.NEGATIVE;
perfer = XYEnum.X;
}

Buffer[] xlink = new Buffer[]{cur.bufferxneglink,null,cur.bufferxposlink};
Buffer[] ylink = new Buffer[]{cur.bufferyneglink,null,cur.bufferyposlink};

int[] xlinknode = new int[]{cur.linkxneg,-1,cur.linkxpos};
int[] ylinknode = new int[]{cur.linkyneg,-1,cur.linkypos};

int[] virtualch = new int[]{R2,0,R1};

if (Main.ALGORITHM == 2){
vchx = R1;
vchy = R1;
Buffer xBuffer = xlink[var1.getValue()];
Buffer yBuffer = ylink[var2.getValue()];
int xNode = xlinknode[var1.getValue()];
int yNode = ylinknode[var2.getValue()];
bufferslc = prefer(xBuffer,yBuffer,vchx,vchy,next.getBuff(),perfer.getValue());
switch (bufferslc){
case 0:
next.setNode(-1);
break;
// 走x方向
case 1:
next.setBuff(xBuffer);
next.setNode(xNode);
next.setChannel(vchx);
break;
// 走y方向
case 2:
next.setBuff(yBuffer);
next.setNode(yNode);
next.setChannel(vchy);
break;
}
//System.out.println(next);
return next;
}

return next;
}

3.2 Mesh与Node

本实验定义了8*8的2d Mesh网络结构,其存放了64个网络节点,每个节点假设其坐标为(x,y),那么此节点在网络中的编号定义为:x*8+y

网络类定义具体如下:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/**
* 二维网格类,用于创建和管理一个二维网格系统。
*/
public class Q2DMesh {

private int k; // 网格的大小(边长)
private Q2DMeshNode[] head; // 网格节点数组,存储网格的头部节点

/**
* 构造函数,初始化一个二维网格。
*
* @param n 网格的边长
* @param R1buffer R1缓冲区大小
* @param R2buffer R2缓冲区大小
*/
public Q2DMesh(int n,int R1buffer,int R2buffer) {
this.k = n;
int t = k * k;
head = new Q2DMeshNode[t];

// 创建所有网格节点并初始化它们的坐标和缓冲区
for (int x = 0; x < k; x++) {
for (int y = 0; y < k; y++) {
int id = x + y*k;
Q2DMeshNode node = new Q2DMeshNode();
node.setMesh(this);
node.setCoordinate(id,x,y);
node.setBuffer(R1buffer,R2buffer);
head[id] = node;
}
}

// 为每个节点设置四个方向链路的缓冲区
for (int x = 0; x < k; x++) {
for (int y = 0; y < k; y++) {
int id = x + y*k;
int xneg, xpos , yneg, ypos;

// 计算节点的四个相邻节点的索引
if (x!=0) xneg = (x-1) + y*k;
else xneg = -1;
if (x!=k-1) xpos = (x+1) + y*k;
else xpos = -1;

if (y!=0) yneg = x + (y-1)*k;
else yneg = -1;
if (y!=k-1) ypos = x + (y+1)*k;
else ypos = -1;
head[id].setLinkBuffer(xneg,xpos,yneg,ypos);
}
}

}

/**
* 获取网格的边长。
*
* @return 网格的边长
*/
public int getK() {
return k;
}

/**
* 获取网格节点数组。
*
* @return 网格节点数组
*/
public Q2DMeshNode[] getHead() {
return head;
}

/**
* 清除所有节点的缓冲区。
*/
public void clearAll(){
for (int i = 0; i < k*k; i++) {
head[i].clearBuffer();
}
}
}

节点类定义具体如下:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package com.leggasai.network;

/**
* Q2DMeshNode类代表了一个二维网格网络中的节点。
* 节点包含自身的缓冲区、与邻居节点的链接信息,以及节点的位置信息。
*/
public class Q2DMeshNode {

// 消息最大长度
public static int MESSLENGTH = 16;

// 频道标识
public static int R1 = 1;
public static int R2 = 2;

// 节点ID
private int nodeId;

// 缓冲区:x轴负方向
public Buffer bufferxneg;
// 缓冲区:x轴正方向
public Buffer bufferxpos;
// 缓冲区:y轴负方向
public Buffer bufferyneg;
// 缓冲区:y轴正方向
public Buffer bufferypos;

// 链接缓冲区:x轴负方向链接
public Buffer bufferxneglink;
// 链接缓冲区:x轴正方向链接
public Buffer bufferxposlink;
// 链接缓冲区:y轴负方向链接
public Buffer bufferyneglink;
// 链接缓冲区:y轴正方向链接
public Buffer bufferyposlink;

// 链接的节点ID:x轴负方向
public int linkxneg;
// 链接的节点ID:x轴正方向
public int linkxpos;
// 链接的节点ID:y轴负方向
public int linkyneg;
// 链接的节点ID:y轴正方向
public int linkypos;

// 所属的网格对象
private Q2DMesh mesh;

// 节点的x坐标
public int x;
// 节点的y坐标
public int y;

/**
* 设置节点的坐标。
*
* @param nodeId 节点ID
* @param x 节点的x坐标
* @param y 节点的y坐标
*/
public void setCoordinate(int nodeId,int x,int y) {
this.nodeId = nodeId;
this.x = x;
this.y = y;
}

/**
* 设置节点的缓冲区参数。
*
* @param buff1 第一个参数,用于配置缓冲区
* @param buff2 第二个参数,用于配置缓冲区
*/
public void setBuffer(int buff1,int buff2){
// 初始化缓冲区
bufferxneg = new Buffer();
bufferxpos = new Buffer();
bufferyneg = new Buffer();
bufferypos = new Buffer();
clearBuffer(); // 清除缓冲区状态

// 设置缓冲区参数
bufferxneg.r1 = buff1;
bufferxpos.r1 = buff1;
bufferyneg.r1 = buff1;
bufferypos.r1 = buff1;

bufferxneg.r2 = buff2;
bufferxpos.r2 = buff2;
bufferyneg.r2 = buff2;
bufferypos.r2 = buff2;

bufferxneg.c = (buff1+buff2) / MESSLENGTH;
bufferxpos.c = (buff1+buff2) / MESSLENGTH;

3.3 Message

用于模拟在网络中传输的消息。

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package com.leggasai.network;

/**
* 消息类,用于在网络中传输消息数据。
*/
public class Message {

// 消息处理时间常量
public static final int PROCESSTIME = 16;
// 消息超时时间常量
public static final int TIMEOUT = 50000;

/**
* 消息的长度
*/
private int length;
/**
* 消息来源节点
*/
private int src;
/**
* 消息目标节点
*/
private int dst;
/**
* 消息超时时间
*/
private int timeout;
/**
* 消息开始传输时间
*/
private int begintrans;
/**
* 消息当前处理步骤
*/
private int step;
/**
* 消息是否处于活动状态
*/
private boolean active;
/**
* 消息路由路径上的节点信息数组
*/
private NodeInfo[] routePath;
/**
* 路由路径上节点的计数
*/
private int count;
/**
* 是否释放链路资源的标志
*/
private boolean releaselink;

/**
* 转向标志,用于网络中的方向调整
*/
private boolean turn;

/**
* 默认构造函数,初始化消息来源和目标为-1。
*/
public Message() {
this.src = -1;
this.dst = -1;
}

/**
* 构造函数,初始化消息的来源和目标节点。
*
* @param src 消息来源节点
* @param dst 消息目标节点
*/
public Message(int src, int dst) {
this.src = src;
this.dst = dst;
begintrans = PROCESSTIME;
routePath = new NodeInfo[Q2DMeshNode.MESSLENGTH];
// 初始化路由路径上的节点信息
for (int i = 0; i < Q2DMeshNode.MESSLENGTH; i++) {
NodeInfo nodeInfo = new NodeInfo();
nodeInfo.setNode(src);
nodeInfo.setChannel(0);
nodeInfo.setBuff(null);
routePath[i] = nodeInfo;
}
this.step = 0;
this.active = true;
this.length = Q2DMeshNode.MESSLENGTH;
this.count = 0;
this.releaselink = false;
this.turn = true;
this.timeout = 0;
}

3.4 Event类

Event类主要用于生成消息以及调用路由算法进行消息传播。

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package com.leggasai.network;

import com.leggasai.network.route.AbstractRouting;
import com.leggasai.network.route.IRouting;

import java.util.Random;

/**
* 事件类,用于处理消息的生成和转发。
* @Author: Jiang Yichen
* @Date: 2024-05-18-14:05
* @Description:
*/
public class Event {
private static final Random rand = new Random();
private int consumed; // 已消费的消息数
private double totalcir; // 总循环次数
private int messarrive; // 到达的消息数
private int k; // 网络中节点的维度
private Q2DMesh mesh; // 网络拓扑
private AbstractRouting route; // 路由算法实例

/**
* 构造函数,初始化事件对象。
* @param route 路由算法实例
*/
public Event(AbstractRouting route) {
this.route = route;
this.mesh = route.getMesh();
this.k = route.getK();
}

/**
* 生成一条消息,实现均匀流量模式。
* @return 生成的消息对象
*/
public Message genMes(){
// 实现uniform流量模式
int tempRand = Math.abs(rand.nextInt()) % (k*k);
int src,dest;
src = tempRand;
while (true){
tempRand = Math.abs(rand.nextInt()) % (k*k);
if (tempRand != src){
dest = tempRand;
break;
}
}
return new Message(src,dest);
}

/**
* 转发消息。
* @param s 待转发的消息对象
*/
void forwardMes(Message s){
// 检查消息是否开始传输,更新传输状态
if(s.getBegintrans() <= 0) s.setCount(s.getCount()+1);

// 在源节点进行特殊处理
if(s.getRoutePath()[0].getNode() == s.getSrc()){
// 若尚未开始传输,则准备传输
if(s.getBegintrans() > 0){
s.setBegintrans(s.getBegintrans()-1);
}else{
s.setBegintrans(s.getBegintrans()-1);
NodeInfo next = route.forward(s);
// 若无法找到下一跳节点,超时计数器加1
if (next.getNode() == -1){
s.setTimeout(s.getTimeout()+1);
}else{
// 成功找到下一跳,重置超时计数器
s.setTimeout(0);
s.getRoutePath()[0]=next;
}
}
}
// 在非源节点进行转发逻辑处理
else{
// 若当前节点不是目的节点,则尝试转发
if (s.getRoutePath()[0].getNode() != s.getDst()){
NodeInfo next = null;
if (route!=null){
next = route.forward(s);
}
// 无法找到下一跳节点,消息进入重试逻辑
if (next.getNode() == -1){
int i;
for(i=1;i<Q2DMeshNode.MESSLENGTH&&s.getRoutePath()[i].getNode()==s.getRoutePath()[0].getNode();i++);
if(i < Q2DMeshNode.MESSLENGTH){
// 处理消息路径中的环路
NodeInfo temp1,temp2;
temp2 = s.getRoutePath()[i-1];
while (i<Q2DMeshNode.MESSLENGTH){
temp1 = s.getRoutePath()[i];
s.getRoutePath()[i] = temp2;
temp2 = temp1;
i++;
}
if (temp2.getNode()!=s.getRoutePath()[Q2DMeshNode.MESSLENGTH-1].getNode()){
s.setReleaselink(true);
}
// 缓存处理
if (temp2.getBuff()!=null && temp2.getNode()!=s.getRoutePath()[Q2DMeshNode.MESSLENGTH-1].getNode()){
temp2.getBuff().bufferPlus(temp2.getChannel(),Q2DMeshNode.MESSLENGTH);
}

}

s.setTimeout(s.getTimeout()+1);
}
// 成功找到下一跳节点,进行路径更新和缓存处理
else{
// 处理路径更新和缓存
NodeInfo temp1,temp2;
temp2 = s.getRoutePath()[0];
int i= 1;
while (i<Q2DMeshNode.MESSLENGTH){
temp1 = s.getRoutePath()[i];
s.getRoutePath()[i] = temp2;
temp2 = temp1;
i++;
}
if (temp2.getNode()!=s.getRoutePath()[Q2DMeshNode.MESSLENGTH-1].getNode()){
s.setReleaselink(true);
}
// 缓存处理
if (temp2.getBuff()!=null && temp2.getNode()!=s.getRoutePath()[Q2DMeshNode.MESSLENGTH-1].getNode()){
temp2.getBuff().bufferPlus(temp2.getChannel(),Q2DMeshNode.MESSLENGTH);
}
s.getRoutePath()[0]=next;
}
}
// 在目的节点进行消息接收处理
else{
NodeInfo temp1,temp2;
temp2 = s.getRoutePath()[0];
int i;
for(i=1;i<Q2DMeshNode.MESSLENGTH&&s.getRoutePath()[i].getNode()==s.getRoutePath()[0].getNode();i++);
if (i==Q2DMeshNode.MESSLENGTH){
// 完成消息传输,更新缓存
s.getRoutePath()[i-1].getBuff().bufferPlus(s.getRoutePath()[i-1].getChannel(),Q2DMeshNode.MESSLENGTH);
s.setActive(false);
totalcir += s.getCount();
messarrive++;
return;
}
// 处理路径更新和缓存
while (i<Q2DMeshNode.MESSLENGTH){
temp1 = s.getRoutePath()[i];
s.getRoutePath()[i] = temp2;
temp2 = temp1;
i++;
}
if (temp2.getNode()!=s.getRoutePath()[Q2DMeshNode.MESSLENGTH-1].getNode()){
s.setReleaselink(true);
}
// 缓存处理
if (temp2.getBuff()!=null && temp2.getNode()!=s.getRoutePath()[Q2DMeshNode.MESSLENGTH-1].getNode()){
temp2.getBuff().bufferPlus(temp2.getChannel(),Q2DMeshNode.MESSLENGTH);
}
}
}
}
}

4. 结果分析

通过对比上述不同路由算法可以得出初步结论:

  • 在注入率和平均时延变化曲线来看:三者总体差距不大。且在注入率为0.5附近时,平均时延急剧升高。可能的原因:
    1. 网络拥塞: 当注入率达到一定水平时,可能会导致网络拥塞。在注入率为0.5附近,网络中的消息数量达到了临界点,超过了系统的处理能力,导致消息传输出现阻塞和延迟增加的情况。
    2. 消息碰撞: 在高注入率下,可能会出现大量的消息碰撞现象,即多个消息同时到达目标节点,导致目标节点无法同时处理所有消息,从而造成延迟增加。
    3. 资源竞争: 随着注入率的增加,网络中的节点之间竞争资源的情况也会增加。在注入率为0.5附近,节点之间的竞争可能达到了高峰,导致了消息传输的延迟增加。
  • 在注入率和吞吐量变化曲线来看:大致的饱和点在0.6附近。Negative-First算法对比其他两种算法稍优,特别是在注入率较高的水平下,而其他两个算法吞吐量几乎一致。其可能的原因为负向优先算法具有一定的灵活性,可以根据实际情况进行动态调整。在高注入率下,负向优先算法可能会根据网络负载情况动态选择合适的路径(绕开高负载节点),以最大程度地减少延迟和提高吞吐量。

总体而言上述三种路由算法的性能差距不明显,其原因可能在于它们路径选择的相似性。

  • 都是基于简单转弯模型的,并根据相似的规则进行路由。
  • 无法进行自适应变化,如果考虑更高的性能,可以采用自适应路由算法。
  • 网络结构以及参数原因:本实验中节点和链路的缓存设置较小,可能对实验结果产生一定影响。

5. 总结

本次实验通过编写Java程序来模拟NoC片上网络传输。实现了Negative-First for 2D Meshes算法,,通过评估平均延迟、吞吐量和饱和点对比了不同路由算法在不同负载情况下的性能表现。之后会尝试进一步研究和优化路由算法,比如采用自适应的路由算法等,以提高片上网络的性能和效率,同时可以考虑更多的性能指标和实验条件,以更全面地评估系统的性能。