[RPRWG] Simula-RPR Shaper error and simulation results.
1.
I have corrected a major error in my shaper class. It did not wake
up a client waiting for the shaper value to reach the lowLimit. Now
it does so (indirectly via the mac data path)
For those of you interested in simulation I have implemented the
shaper so that it is correct down to the byte/one credit level.
Whenever it is used it remembers the current credit value and the
current time. The next time it is used ut is first updated by using
the previous credit value, the time this value was correct and the
add/increment rate. In this way it is always correct and at the samme
time consums less simulation resources.
The new code is at the end of this message.
2.
I have re-run the scenario by DVJ and Harry Peng , now with fewer active
stations (because this is easier on my laptop), and a class A1 access
delay timer set to 20 microsec.
The results can be found at
www.ifi.uio.no/~steing/rpr/sim_res3.pdf
as soon as I can transfer it over this tiny line that is my current Internet
connection.
In the scenario I have 8 greedy C stations (1 thru 8), 8 stations sending A1
traffic (53 thru 60) at 3% and one station (61) sending A0 traffic at 1%.
a) the A0 traffic goes perfectly.
b) the A1 traffic goes as expected: Because it is shaped by shaper D,
it stops when D goes below loLimit, and the A1 traffic stops until the
access delay timer sense a congestion and fairness message has time to
take effect (10 ms to send the message upstream and a little mor to stop the
C traffic. The graphs show packets from stations 1 thru 8 and 53 thru 60
as they enter station 62. They also show the value of the shaper in
station 60 and the size of the STQ in station 60.
Stein
===============================
import java.io.*;
import java.util.*;
/**
* Shaper class
SG 22/7/03: Made as a process in order to wake up the Mac client
(indirectly through the Mac data path) that wants to send a packet when
the shaper value allows.
New methods used internally: act() and shaperSchedule()
*/
public class Shaper extends Unit{
public long startTime;
public long credit=0;
/**
* The shapers incremental-rate in bytes/second
*/
public long addRate;
public int serviceClass;
public long incSize;
public long hiLimit, loLimit;
public Kernel sim;
public Datapath dp;
// mostly for debug purposes:
public long nextWakeTime = 0;
public boolean running;
/**
* Creates a shaper
* @param s The simulator engine
* @param d The datapath this shaper belongs to
* @param start The initial credit value for the shaper
* @param low This low limit on this shaper
* @param high The high limit on this shaper
*/
public Shaper(Kernel s, Datapath d, long start, long low, long high) {
sim = s; dp = d;
running = false;
loLimit = low;
hiLimit = high;
credit = start;
Reporter.reportShaper(this);
}
/**
* Used by the implementation to make this shaper know wich shaper it
* handles frames for. shaperD shaperM etc..
*/
public void setClass(int serviceClass) {
this.serviceClass = serviceClass;
}
/*
* newRate is of unit Bytes (or credits) / (ageCoef * aginInterval).
Completely new method July 21. 2003
*/
public void setRateInit(long newRate) {
//SG 21/7/03
double temp = (double)newRate;
// one second is 1000000000. nanosec
// remember addRate is in credits per second
addRate = (long)(temp / (Const.AGING_INTERVAL * Const.AGE_COEF / 1000000000. ));
//SG 21/7/03
update();
}
/*
* newRate is of unit Bytes (or credits) / (ageCoef * aginInterval).
*/
public void setRate(long newRate) {
//SG 21/7/03
update(); // moved from 7 lines below
double temp = (double)newRate;
// one second is 1000000000. nanosec
// remember addRate is in credits per second
addRate = (long)(1000000000. * temp / (Const.AGING_INTERVAL * Const.AGE_COEF));
//SG 21/7/03
shaperSchedule();
// update();
}
public boolean pass() {
update();
return credit >= loLimit;
}
/**
* Returns the send value for all other shapers than FE shaper. This is because
* FE shaper returns an integer while the others return a boolean value
*/
public boolean send() {
if (serviceClass == Const.CLASS_C)
Const.error("Should not be here. Use passC() instead");
update();
if (serviceClass == Const.CLASS_A0) dp.shaperA1.update();
if (serviceClass == Const.CLASS_A1) dp.shaperA0.update();
boolean send = true;
switch(serviceClass) {
case Const.MAC:
send = credit >= loLimit;
break;
case Const.CLASS_A0:
case Const.CLASS_A1:
send = (dp.shaperA0.credit >= dp.shaperA0.loLimit) ||
((dp.shaperA1.credit >= dp.shaperA1.loLimit) && dp.shaperD.pass());
break;
case Const.CLASS_B:
send = ((credit >= loLimit) && dp.shaperD.pass());
break;
case Const.DOWN:
send = credit >= loLimit;
break;
default:
Const.error("Shaper.send()");
}
return send;
}
/**
* Returns the correct value for the FE shaper. This is the only shaper
* that is supposed to use this method because it is the only one returning
* an integer.
*/
public int sendC() {
update();
if (serviceClass != Const.CLASS_C)
Const.error("Should not be here. Use pass() instead");
if (dp.fair.addRateOK() && dp.fair.addRateCongestedOK( ) && dp.shaperD.pass()) {
return Const.MAX_STATIONS;
}
if (dp.fair.addRateOK( ) && dp.shaperD.pass()){
return dp.fair.hopsToCongestion;
}
else {
return 0;
}
}
public void decreaseCredits(int cred) {
update();
credit -= cred;
running = true;
Reporter.reportShaper(this);
shaperSchedule();
}
boolean entryAvailable() {
switch(serviceClass) {
case Const.IDLE:
return false;
case Const.MAC:
return !dp.addMac.empty();
case Const.CLASS_A0:
case Const.CLASS_A1:
return dp.clnt.classAEntryAvailable(dp.myRingletID);
case Const.CLASS_B:
return (dp.clnt.classBEntryAvailable(dp.myRingletID) != null);
case Const.CLASS_C:
return (dp.clnt.classCEntryAvailable(dp.myRingletID) != null);
case Const.DOWN:
return (dp.clnt.classAEntryAvailable(dp.myRingletID) ||
(dp.clnt.classBEntryAvailable(dp.myRingletID) != null) ||
(dp.clnt.classCEntryAvailable(dp.myRingletID) != null) ||
(Const.shapeStq && dp.EntryInSTQ())
);
default:
Const.error("Shaper.running(): Unknown shaper class: "+ serviceClass);
}
return true;
}
public void update() {
if (!entryAvailable() && credit > loLimit) {
credit = loLimit;
running = false;
}
if (running) {
long raise = (long)(((sim.time-startTime)*addRate)/1000000000.+1);
switch (serviceClass) {
case Const.IDLE:
break;
case Const.MAC:
credit = Math.min(hiLimit, credit+raise);
break;
case Const.CLASS_A0:
credit = Math.min(hiLimit, credit+raise);
dp.shaperA1.credit =
Math.min(dp.shaperA1.hiLimit,
dp.shaperA1.credit+raise);
break;
case Const.CLASS_A1:
credit = Math.min(hiLimit, credit+raise);
dp.shaperA0.credit =
Math.min(dp.shaperA0.hiLimit,
dp.shaperA0.credit+raise);
break;
case Const.CLASS_B:
credit = Math.min(hiLimit, credit+raise);
break;
case Const.CLASS_C:
credit = Math.min(hiLimit, credit+raise);
break;
case Const.DOWN:
credit = Math.min(hiLimit, credit+raise);
break;
default:
Const.error("Should not be here");
}
}
startTime = sim.time;
Reporter.reportShaper(this);
}
//SG 21/7/03
public void shaperSchedule()
{ long timeToLim; long length = 0;
// update();
if (credit < loLimit)
length = loLimit - credit;
else if ((credit < hiLimit) && entryAvailable())
length = hiLimit - credit;
else {length = 0; running = false;}
// Const.report("Shaper scheduled for time: " +length + " time units" +
// ". Credits left: " + credit );
// if (addRate <= 0) System.out.println(" WARNING. Shaper rate zero ");
if (length != 0 && addRate > 0)
{ running = true;
// timeToLim = Math.round(length/(addRate/400000));
//SG 21/7/03 : Is this correct ???? . BETTER NOW:
timeToLim = Math.round(length * 1000000000./addRate +1);
if (sim.time + timeToLim + 1 < 0) System.out.println(
"Time to lim: " + timeToLim + " " + length + " " + addRate + " " + credit);
nextWakeTime = sim.time + timeToLim ;
// System.out.println("Shaper Sched " + sim.time + " " + sim.time + timeToLim );
sim.reschedule (this, sim.time + timeToLim );
}
}
//SG 21/7/03
public void act()
{ if (running)
{ // Const.report(" Shaper activated at time "+ sim.time);
update();
shaperSchedule();
dp.canISend();
}
}
} // end class