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
△X=0 && △Y=0
:当前节点即目的地,已经到达。
△X>0 && △Y=0
:目标节点在当前节点X+方向,数据包沿着 X+方向进行发送,如果在发送的过程中,遇到了故障链路或者缓冲已满,则沿着 Y-方向进行一个跳步
△X<0 && △Y=0
:目标节点在当前节点X-方向,数据包沿着X-方向进行发送,如果在发送的过程中,遇到了故障链路或者缓冲已满,则沿着Y-方向进行一个跳步
△X=0 && △Y>0
:目标节点在当前节点Y+方向,数据包沿着Y+方向进行发送,如果在发送的过程中,遇到了故障链路或者缓冲已满,则沿着X-方向进行一个跳步
△X=0 && △Y<0
:目标节点在当前节点Y-方向,数据包沿着Y-方向进行发送,如果在发送的过程中,遇到了故障链路或者缓冲已满,则沿着X-方向进行一个跳步
△X>0 && △Y>0
:目标节点在当前节点东北方,数据包沿着X+/Y+方向进行发送,如果在发送的过程中,遇到了故障链路或者缓冲已满,则沿着X-/Y-方向进行一个跳步。这里规定优先按X方向进行。
△X>0 && △Y<0
:目标节点在当前节点东南方,数据包沿着Y-方向进行发送,如果在发送的过程中,遇到了故障链路或者缓冲已满,则沿着X-方向进行一个跳步。这里规定优先按Y方向进行。
△X<0 && △Y>0
:目标节点在当前节点西北方,数据包沿着X-方向进行发送,如果在发送的过程中,遇到了故障链路或者缓冲已满,则沿着Y-方向进行一个跳步。这里规定优先按X方向进行。
△X<0 && △Y<0
:目标节点在当前节点西南方,数据包沿着X-/Y-方向进行发送,如果在发送的过程中,遇到了故障链路或者缓冲已满,则沿着另一方向的负方向进行一个跳步。这里规定优先按X方向进行。
3. 算法实现 3.1 路由相关类
IRouting
:定义了顶层接口,提供forward
方法,接收message类型的参数,用于消息传播。
AbstractRouting
:抽象父类,包含了一些公有属性和方法。
NegativeFirstRouting
、XYRouting
、WestFirstRouting
:路由子类,实现了不同的路由算法。在本实验中,主要分析NegativeFirst算法
,另外两个主要用于结果对比。
具体路由选择代码如下:
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) { int curx = cur.x; int cury = cur.y; int dstx = dst.x; int dsty = dst.y; int xdis = dstx - curx; int ydis = dsty - cury; int bufferslc; DirectionEnum var1=DirectionEnum.NONE,var2=DirectionEnum.NONE; int vchx=0 ,vchy=0 ; XYEnum perfer = XYEnum.O; if (xdis==0 && ydis==0 ){ var1 = DirectionEnum.NONE; var2 = DirectionEnum.NONE; perfer = XYEnum.O; } else if (xdis>0 && ydis==0 ){ var1 = DirectionEnum.POSITIVE; var2 = DirectionEnum.NEGATIVE; perfer = XYEnum.X; } else if (xdis<0 && ydis==0 ){ var1 = DirectionEnum.NEGATIVE; var2 = DirectionEnum.NEGATIVE; perfer = XYEnum.X; } else if (xdis==0 && ydis>0 ){ var1 = DirectionEnum.NEGATIVE; var2 = DirectionEnum.POSITIVE; perfer = XYEnum.Y; } else if (xdis==0 && ydis<0 ){ var1 = DirectionEnum.NEGATIVE; var2 = DirectionEnum.NEGATIVE; perfer = XYEnum.Y; } else if (xdis>0 && ydis>0 ){ var1 = DirectionEnum.POSITIVE; var2 = DirectionEnum.POSITIVE; perfer = XYEnum.Y; } else if (xdis<0 && ydis<0 ){ var1 = DirectionEnum.NEGATIVE; var2 = DirectionEnum.NEGATIVE; perfer = XYEnum.X; } else if (xdis>0 && ydis<0 ){ var1 = DirectionEnum.NEGATIVE; var2 = DirectionEnum.NEGATIVE; perfer = XYEnum.Y; } else if (xdis<0 && ydis>0 ){ var1 = DirectionEnum.NEGATIVE; 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 ; case 1 : next.setBuff(xBuffer); next.setNode(xNode); next.setChannel(vchx); break ; case 2 : next.setBuff(yBuffer); next.setNode(yNode); next.setChannel(vchy); break ; } return next; } return next; }
3.2 Mesh与Node 本实验定义了8*8
的2d Mesh网络结构,其存放了64个网络节点,每个节点假设其坐标为(x,y)
,那么此节点在网络中的编号定义为:x*8+y
。
网络类定义具体如下:
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; 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); } } } public int getK () { return k; } public Q2DMeshNode[] getHead() { return head; } public void clearAll () { for (int i = 0 ; i < k*k; i++) { head[i].clearBuffer(); } } }
节点类定义具体如下:
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;public class Q2DMeshNode { public static int MESSLENGTH = 16 ; public static int R1 = 1 ; public static int R2 = 2 ; private int nodeId; public Buffer bufferxneg; public Buffer bufferxpos; public Buffer bufferyneg; public Buffer bufferypos; public Buffer bufferxneglink; public Buffer bufferxposlink; public Buffer bufferyneglink; public Buffer bufferyposlink; public int linkxneg; public int linkxpos; public int linkyneg; public int linkypos; private Q2DMesh mesh; public int x; public int y; public void setCoordinate (int nodeId,int x,int y) { this .nodeId = nodeId; this .x = x; this .y = y; } 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 用于模拟在网络中传输的消息。
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; public Message () { this .src = -1 ; this .dst = -1 ; } 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类主要用于生成消息以及调用路由算法进行消息传播。
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;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; public Event (AbstractRouting route) { this .route = route; this .mesh = route.getMesh(); this .k = route.getK(); } public Message genMes () { 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); } 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); 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附近时,平均时延急剧升高。可能的原因:
网络拥塞: 当注入率达到一定水平时,可能会导致网络拥塞。在注入率为0.5附近,网络中的消息数量达到了临界点,超过了系统的处理能力,导致消息传输出现阻塞和延迟增加的情况。
消息碰撞: 在高注入率下,可能会出现大量的消息碰撞现象,即多个消息同时到达目标节点,导致目标节点无法同时处理所有消息,从而造成延迟增加。
资源竞争: 随着注入率的增加,网络中的节点之间竞争资源的情况也会增加。在注入率为0.5附近,节点之间的竞争可能达到了高峰,导致了消息传输的延迟增加。
在注入率和吞吐量变化曲线来看:大致的饱和点在0.6附近。Negative-First
算法对比其他两种算法稍优,特别是在注入率较高的水平下,而其他两个算法吞吐量几乎一致。其可能的原因为负向优先算法具有一定的灵活性,可以根据实际情况进行动态调整。在高注入率下,负向优先算法可能会根据网络负载情况动态选择合适的路径(绕开高负载节点),以最大程度地减少延迟和提高吞吐量。
总体而言上述三种路由算法的性能差距不明显,其原因可能在于它们路径选择的相似性。
都是基于简单转弯模型的,并根据相似的规则进行路由。
无法进行自适应变化,如果考虑更高的性能,可以采用自适应路由算法。
网络结构以及参数原因:本实验中节点和链路的缓存设置较小,可能对实验结果产生一定影响。
5. 总结 本次实验通过编写Java程序来模拟NoC片上网络传输。实现了Negative-First for 2D Meshes
算法,,通过评估平均延迟、吞吐量和饱和点对比了不同路由算法在不同负载情况下的性能表现。之后会尝试进一步研究和优化路由算法,比如采用自适应的路由算法等,以提高片上网络的性能和效率,同时可以考虑更多的性能指标和实验条件,以更全面地评估系统的性能。