Changed logic for Unavailable
This commit is contained in:
@@ -30,19 +30,6 @@ public class Event implements Comparable<Event> {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new event.
|
||||
*
|
||||
* @param node The node that the event is associated with.
|
||||
* @param time The time at which the event occurs.
|
||||
* @param type The type of event.
|
||||
*
|
||||
* @return The new event.
|
||||
*/
|
||||
public static Event newType(ServerNode node, double time, Type type) {
|
||||
return new Event(type, node, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new arrival event.
|
||||
*
|
||||
@@ -65,11 +52,23 @@ public class Event implements Comparable<Event> {
|
||||
return new Event(Type.DEPARTURE, node, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new available event.
|
||||
*
|
||||
* @param node The node that the event is associated with.
|
||||
* @param time The time at which the event occurs.
|
||||
* @return The new event.
|
||||
*/
|
||||
public static Event newAvailable(ServerNode node, double time) {
|
||||
return new Event(Type.AVAILABLE, node, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of event.
|
||||
*/
|
||||
public static enum Type {
|
||||
ARRIVAL,
|
||||
DEPARTURE,
|
||||
AVAILABLE,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ public class ServerNode {
|
||||
public final String name;
|
||||
public final int maxServers;
|
||||
public final int spawnArrivals;
|
||||
public final Distribution distribution;
|
||||
public final Distribution service;
|
||||
public final Distribution unavailable;
|
||||
|
||||
/**
|
||||
* Creates a source node with the given name and distribution.
|
||||
@@ -23,7 +24,7 @@ public class ServerNode {
|
||||
* @return The created source node.
|
||||
*/
|
||||
public static ServerNode createSource(String name, Distribution distribution) {
|
||||
return new ServerNode(name, Integer.MAX_VALUE, distribution, Integer.MAX_VALUE);
|
||||
return new ServerNode(name, Integer.MAX_VALUE, distribution, null, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,25 +32,39 @@ public class ServerNode {
|
||||
* arrivals to spawn that are served by infinite servers (Integer.MAX_VALUE).
|
||||
*
|
||||
* @param name The name of the node.
|
||||
* @param distribution The distribution of the inter-arrival times.
|
||||
* @param service The distribution of the inter-arrival times.
|
||||
* @param spawnArrivals The number of arrivals to spawn.
|
||||
* @return The created source node.
|
||||
*/
|
||||
public static ServerNode createLimitedSource(String name, Distribution distribution, int spawnArrivals) {
|
||||
return new ServerNode(name, Integer.MAX_VALUE, distribution, spawnArrivals);
|
||||
public static ServerNode createLimitedSource(String name, Distribution service, int spawnArrivals) {
|
||||
return new ServerNode(name, Integer.MAX_VALUE, service, null, spawnArrivals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a queue node with the given name, maximum number of servers, and
|
||||
* distribution.
|
||||
*
|
||||
* @param name The name of the node.
|
||||
* @param maxServers The maximum number of servers in the queue.
|
||||
* @param distribution The distribution of the service times.
|
||||
* @param name The name of the node.
|
||||
* @param maxServers The maximum number of servers in the queue.
|
||||
* @param service The distribution of the service times.
|
||||
* @return The created queue node.
|
||||
*/
|
||||
public static ServerNode createQueue(String name, int maxServers, Distribution distribution) {
|
||||
return new ServerNode(name, maxServers, distribution, 0);
|
||||
public static ServerNode createQueue(String name, int maxServers, Distribution service) {
|
||||
return new ServerNode(name, maxServers, service, null, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a queue node with the given name, maximum number of servers, and
|
||||
* distribution.
|
||||
*
|
||||
* @param name The name of the node.
|
||||
* @param maxServers The maximum number of servers in the queue.
|
||||
* @param service The distribution of the service times.
|
||||
* @param unavailable The distribution of the unavailable times after service.
|
||||
* @return The created queue node.
|
||||
*/
|
||||
public static ServerNode createQueue(String name, int maxServers, Distribution service, Distribution unavailable) {
|
||||
return new ServerNode(name, maxServers, service, unavailable, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,13 +75,14 @@ public class ServerNode {
|
||||
*
|
||||
* @param name The name of the node.
|
||||
* @param maxServers The maximum number of servers in the queue.
|
||||
* @param distribution The distribution of the service times.
|
||||
* @param service The distribution of the service times.
|
||||
* @param unavailable The distribution of the unavailable times after service.
|
||||
* @param spawnArrivals The number of arrivals to spawn.
|
||||
* @throws NullPointerException if the distribution is null
|
||||
*/
|
||||
public ServerNode(String name, int maxServers, Distribution distribution, int spawnArrivals) {
|
||||
if (distribution == null)
|
||||
throw new NullPointerException("Distribution can't be null");
|
||||
private ServerNode(String name, int maxServers, Distribution service, Distribution unavailable, int spawnArrivals) {
|
||||
if (service == null)
|
||||
throw new NullPointerException("Service distribution can't be null");
|
||||
if (maxServers <= 0)
|
||||
maxServers = 1;
|
||||
if (spawnArrivals < 0)
|
||||
@@ -74,8 +90,9 @@ public class ServerNode {
|
||||
|
||||
this.name = name;
|
||||
this.maxServers = maxServers;
|
||||
this.distribution = distribution;
|
||||
this.spawnArrivals = spawnArrivals;
|
||||
this.service = service;
|
||||
this.unavailable = unavailable;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,12 +103,18 @@ public class ServerNode {
|
||||
* @param rng The random number generator to use.
|
||||
* @return A positive sample from the distribution.
|
||||
*/
|
||||
public double getPositiveSample(Rng rng) {
|
||||
double sample;
|
||||
do {
|
||||
sample = this.distribution.sample(rng);
|
||||
} while (sample < 0);
|
||||
return sample;
|
||||
public double getServiceTime(Rng rng) {
|
||||
return Distribution.getPositiveSample(this.service, rng);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the unavailable time after a service
|
||||
*
|
||||
* @param rng The random number generator to use.
|
||||
* @return A positive or 0 value from the distribution.
|
||||
*/
|
||||
public double getUnavailableTime(Rng rng) {
|
||||
return Distribution.getPositiveSample(this.unavailable, rng);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,6 +128,14 @@ public class ServerNode {
|
||||
return this.spawnArrivals > Math.max(0, numArrivals);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof ServerNode))
|
||||
return false;
|
||||
var other = (ServerNode) obj;
|
||||
return obj.hashCode() == other.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.name.hashCode();
|
||||
|
||||
@@ -26,7 +26,7 @@ public final class Simulation {
|
||||
* Creates a new run of the simulation with the given nodes and random number
|
||||
* generator.
|
||||
*
|
||||
* @param states The nodes in the network.
|
||||
* @param states The nodes in the network.
|
||||
* @param rng The random number generator to use.
|
||||
* @param criterias when the simulation has to end.
|
||||
*/
|
||||
@@ -73,32 +73,32 @@ public final class Simulation {
|
||||
this.time = event.time;
|
||||
|
||||
switch (event.type) {
|
||||
case AVAILABLE -> {
|
||||
state.stats.updateTimes(this.time, state.numServerBusy, state.numServerUnavailable, node.maxServers);
|
||||
state.numServerUnavailable--;
|
||||
this.addDepartureIfPossible(node, state);
|
||||
}
|
||||
case ARRIVAL -> {
|
||||
state.queue.add(this.time);
|
||||
state.stats.updateArrival(this.time, state.queue.size(), state.numServerBusy != 0);
|
||||
|
||||
if (state.numServerBusy < node.maxServers) {
|
||||
state.numServerBusy++;
|
||||
this.addDeparture(node);
|
||||
}
|
||||
state.stats.updateArrival(this.time, state.queue.size());
|
||||
state.stats.updateTimes(this.time, state.numServerBusy, state.numServerUnavailable, node.maxServers);
|
||||
this.addDepartureIfPossible(node, state);
|
||||
}
|
||||
case DEPARTURE -> {
|
||||
var startService = state.queue.poll();
|
||||
state.stats.updateDeparture(this.time, this.time - startService);
|
||||
var arrivalTime = state.queue.poll();
|
||||
state.stats.updateDeparture(this.time, arrivalTime);
|
||||
state.stats.updateTimes(this.time, state.numServerBusy, state.numServerUnavailable, node.maxServers);
|
||||
state.numServerBusy--;
|
||||
|
||||
if (state.numServerBusy > state.queue.size()) {
|
||||
state.numServerBusy--;
|
||||
} else {
|
||||
this.addDeparture(node);
|
||||
}
|
||||
this.addUnavailableIfPossible(node, state);
|
||||
this.addDepartureIfPossible(node, state);
|
||||
|
||||
var next = this.net.getChildOf(node, this.rng);
|
||||
if (next != null) {
|
||||
if (next != null)
|
||||
this.addArrival(next);
|
||||
}
|
||||
if (node.shouldSpawnArrival(state.stats.numArrivals)) {
|
||||
|
||||
if (node.shouldSpawnArrival(state.stats.numArrivals))
|
||||
this.addArrival(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,14 +149,36 @@ public final class Simulation {
|
||||
|
||||
/**
|
||||
* Adds a departure event to the future event list. The event is created based
|
||||
* on the given node, and the delay is determined by the node's distribution.
|
||||
* on the given node, and the delay is determined by the node's service
|
||||
* distribution.
|
||||
*
|
||||
* @param node The node to create the event for.
|
||||
*/
|
||||
public void addDeparture(ServerNode node) {
|
||||
var delay = node.getPositiveSample(this.rng);
|
||||
var event = Event.newDeparture(node, this.time + delay);
|
||||
fel.add(event);
|
||||
public void addDepartureIfPossible(ServerNode node, NodeState state) {
|
||||
var canServe = node.maxServers > state.numServerBusy + state.numServerUnavailable;
|
||||
var hasRequests = state.queue.size() > state.numServerBusy;
|
||||
|
||||
if (canServe && hasRequests) {
|
||||
state.numServerBusy++;
|
||||
var delay = node.getServiceTime(this.rng);
|
||||
var event = Event.newDeparture(node, this.time + delay);
|
||||
fel.add(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @param node
|
||||
* @param state
|
||||
*/
|
||||
public void addUnavailableIfPossible(ServerNode node, NodeState state) {
|
||||
var delay = node.getUnavailableTime(rng);
|
||||
if (delay > 0) {
|
||||
state.numServerUnavailable++;
|
||||
var event = Event.newAvailable(node, time + delay);
|
||||
this.fel.add(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,7 +205,8 @@ public final class Simulation {
|
||||
*/
|
||||
public static class NodeState {
|
||||
public int numServerBusy = 0;
|
||||
public int numServerUnavailable = 0;
|
||||
public final Statistics stats = new Statistics();
|
||||
private final ArrayDeque<Double> queue = new ArrayDeque<>();
|
||||
public final ArrayDeque<Double> queue = new ArrayDeque<>();
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ public class Result {
|
||||
this.simulationTime = time;
|
||||
this.timeElapsedMS = elapsed;
|
||||
this.nodes = nodes;
|
||||
this.size = (int) Math.ceil(Math.log10(this.simulationTime));
|
||||
this.size = (int) Math.ceil(Math.max(Math.log10(this.simulationTime), 1));
|
||||
this.iFormat = "%" + this.size + ".0f";
|
||||
this.fFormat = "%" + (this.size + 4) + ".3f";
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public class Result {
|
||||
*/
|
||||
public String getSummary() {
|
||||
String[] h = { "Node", "Departures", "Avg Queue", "Avg Wait", "Avg Response", "Throughput", "Utilization %",
|
||||
"Last Event" };
|
||||
"Unavailable %", "Last Event" };
|
||||
var table = new ConsoleTable(h);
|
||||
|
||||
for (var entry : this.nodes.entrySet()) {
|
||||
@@ -70,6 +70,7 @@ public class Result {
|
||||
fFormat.formatted(stats.avgResponse),
|
||||
fFormat.formatted(stats.troughput),
|
||||
fFormat.formatted(stats.utilization * 100),
|
||||
fFormat.formatted(stats.unavailable * 100),
|
||||
fFormat.formatted(stats.lastEventTime));
|
||||
}
|
||||
return table.toString();
|
||||
@@ -86,7 +87,7 @@ public class Result {
|
||||
|
||||
if (tableHeader)
|
||||
builder.append(
|
||||
"Seed,Node,Arrivals,Departures,MaxQueue,AvgQueue,AvgWait,AvgResponse,BusyTime,WaitTime,ResponseTime,LastEventTime,Throughput,Utilization\n");
|
||||
"Seed,Node,Arrivals,Departures,MaxQueue,AvgQueue,AvgWait,AvgResponse,BusyTime,WaitTime,UnavailableTime,ResponseTime,LastEventTime,Throughput,Utilization,Unavailable\n");
|
||||
for (var entry : this.nodes.entrySet()) {
|
||||
var stats = entry.getValue();
|
||||
builder.append(this.seed);
|
||||
@@ -109,6 +110,8 @@ public class Result {
|
||||
builder.append(',');
|
||||
builder.append(stats.waitTime);
|
||||
builder.append(',');
|
||||
builder.append(stats.unavailableTime);
|
||||
builder.append(',');
|
||||
builder.append(stats.responseTime);
|
||||
builder.append(',');
|
||||
builder.append(stats.lastEventTime);
|
||||
@@ -116,6 +119,8 @@ public class Result {
|
||||
builder.append(stats.troughput);
|
||||
builder.append(',');
|
||||
builder.append(stats.utilization);
|
||||
builder.append(',');
|
||||
builder.append(stats.unavailable);
|
||||
builder.append('\n');
|
||||
}
|
||||
return builder.toString();
|
||||
|
||||
@@ -11,6 +11,7 @@ public class Statistics {
|
||||
public double numDepartures = 0.0d;
|
||||
public double maxQueueLength = 0.0d;
|
||||
public double avgQueueLength = 0.0d;
|
||||
public double unavailableTime = 0.0d;
|
||||
public double busyTime = 0.0d;
|
||||
public double waitTime = 0.0d;
|
||||
public double responseTime = 0.0d;
|
||||
@@ -21,6 +22,7 @@ public class Statistics {
|
||||
public double avgResponse = 0.0d;
|
||||
public double troughput = 0.0d;
|
||||
public double utilization = 0.0d;
|
||||
public double unavailable = 0.0d;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
@@ -29,16 +31,12 @@ public class Statistics {
|
||||
* @param newQueueSize
|
||||
* @param updateBusy
|
||||
*/
|
||||
public void updateArrival(double time, double newQueueSize, boolean updateBusy) {
|
||||
public void updateArrival(double time, double newQueueSize) {
|
||||
var total = this.avgQueueLength * this.numArrivals;
|
||||
|
||||
this.numArrivals++;
|
||||
this.avgQueueLength = (total + newQueueSize) / this.numArrivals;
|
||||
this.maxQueueLength = Math.max(this.maxQueueLength, newQueueSize);
|
||||
if (updateBusy)
|
||||
this.busyTime += time - this.lastEventTime;
|
||||
|
||||
this.lastEventTime = time;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,15 +47,30 @@ public class Statistics {
|
||||
*/
|
||||
public void updateDeparture(double time, double response) {
|
||||
this.numDepartures++;
|
||||
this.responseTime += response;
|
||||
this.busyTime += time - this.lastEventTime;
|
||||
this.lastEventTime = time;
|
||||
this.waitTime = this.responseTime - this.busyTime;
|
||||
this.responseTime += time - response;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @param time
|
||||
* @param serverBusy
|
||||
* @param serverUnavailable
|
||||
*/
|
||||
public void updateTimes(double time, int serverBusy, int serverUnavailable, int maxServers) {
|
||||
if (serverBusy > 0)
|
||||
this.busyTime += time - this.lastEventTime;
|
||||
else if (serverUnavailable == maxServers)
|
||||
this.unavailableTime += time - this.lastEventTime;
|
||||
|
||||
this.waitTime = this.responseTime - this.busyTime;
|
||||
this.avgWaitTime = this.waitTime / this.numDepartures;
|
||||
this.avgResponse = this.responseTime / this.numDepartures;
|
||||
this.troughput = this.numDepartures / this.lastEventTime;
|
||||
this.utilization = this.busyTime / this.lastEventTime;
|
||||
this.troughput = this.numDepartures / time;
|
||||
this.utilization = this.busyTime / time;
|
||||
this.unavailable = this.unavailableTime / time;
|
||||
|
||||
this.lastEventTime = time;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,11 +119,13 @@ public class Statistics {
|
||||
save.avgQueueLength = func.apply(val1.avgQueueLength, val2.avgQueueLength);
|
||||
save.busyTime = func.apply(val1.busyTime, val2.busyTime);
|
||||
save.responseTime = func.apply(val1.responseTime, val2.responseTime);
|
||||
save.unavailableTime = func.apply(val1.unavailableTime, val2.unavailableTime);
|
||||
save.waitTime = func.apply(val1.waitTime, val2.waitTime);
|
||||
save.lastEventTime = func.apply(val1.lastEventTime, val2.lastEventTime);
|
||||
// derived stats
|
||||
save.avgWaitTime = func.apply(val1.avgWaitTime, val2.avgWaitTime);
|
||||
save.avgResponse = func.apply(val1.avgResponse, val2.avgResponse);
|
||||
save.unavailable = func.apply(val1.unavailable, val2.unavailable);
|
||||
save.troughput = func.apply(val1.troughput, val2.troughput);
|
||||
save.utilization = func.apply(val1.utilization, val2.utilization);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user