Added Unavailable times
- added event unavailable - added event start time - changed servernode logic for unavailability - changed simulation logic - added unavailable time to results - added unavailable statistics - fixed tests
This commit is contained in:
@@ -29,13 +29,21 @@ public class Main {
|
|||||||
csv = arguments.get("csv");
|
csv = arguments.get("csv");
|
||||||
var parallel = arguments.containsKey("p");
|
var parallel = arguments.containsKey("p");
|
||||||
|
|
||||||
|
// varius distributions
|
||||||
|
var distrExp = new Distribution.Exponential(lambda);
|
||||||
|
var distrNorm = new Distribution.NormalBoxMuller(mu, sigma);
|
||||||
|
var distrUnav = new Distribution.UnavailableTime(0.2, distrNorm);
|
||||||
|
|
||||||
// Build the network
|
// Build the network
|
||||||
var net = new Net();
|
var net = new Net();
|
||||||
var node1 = ServerNode.createLimitedSource("Source", new Distribution.Exponential(lambda), total);
|
var node1 = ServerNode.createLimitedSource("Source", distrExp, total);
|
||||||
var node2 = ServerNode.createQueue("Queue", 1, new Distribution.NormalBoxMuller(mu, sigma));
|
var node2 = ServerNode.createQueue("Queue", 1, distrNorm);
|
||||||
|
var node3 = ServerNode.createQueue("Queue Wait", 1, distrNorm, distrUnav);
|
||||||
net.addNode(node1);
|
net.addNode(node1);
|
||||||
net.addNode(node2);
|
net.addNode(node2);
|
||||||
|
net.addNode(node3);
|
||||||
net.addConnection(node1, node2, 1.0);
|
net.addConnection(node1, node2, 1.0);
|
||||||
|
net.addConnection(node2, node3, 1.0);
|
||||||
net.normalizeWeights();
|
net.normalizeWeights();
|
||||||
|
|
||||||
/// Run multiple simulations
|
/// Run multiple simulations
|
||||||
|
|||||||
@@ -4,8 +4,34 @@ package net.berack.upo.valpre.rand;
|
|||||||
* Represents a probability distribution.
|
* Represents a probability distribution.
|
||||||
*/
|
*/
|
||||||
public interface Distribution {
|
public interface Distribution {
|
||||||
|
/**
|
||||||
|
* Return a sample from the distribution.
|
||||||
|
*
|
||||||
|
* @param rng The random number generator to use.
|
||||||
|
* @return A number given from the distribution.
|
||||||
|
*/
|
||||||
public double sample(Rng rng);
|
public double sample(Rng rng);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a positive sample from the distribution.
|
||||||
|
* This is useful if you need to generate a positive value from a distribution
|
||||||
|
* that can generate negative values. For example, the normal distribution.
|
||||||
|
*
|
||||||
|
* @param distribution The distribution to sample
|
||||||
|
* @param rng The random number generator to use.
|
||||||
|
* @return A positive or 0 value from the distribution.
|
||||||
|
*/
|
||||||
|
public static double getPositiveSample(Distribution distribution, Rng rng) {
|
||||||
|
if (distribution == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
double sample;
|
||||||
|
do {
|
||||||
|
sample = distribution.sample(rng);
|
||||||
|
} while (sample < 0);
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an exponential distribution.
|
* Represents an exponential distribution.
|
||||||
*/
|
*/
|
||||||
@@ -172,4 +198,30 @@ public interface Distribution {
|
|||||||
return -Math.log(rng.random()) / lambdas[i];
|
return -Math.log(rng.random()) / lambdas[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
*/
|
||||||
|
public static class UnavailableTime implements Distribution {
|
||||||
|
public final double probability;
|
||||||
|
public final Distribution distribution;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
*
|
||||||
|
* @param probability
|
||||||
|
* @param distribution
|
||||||
|
*/
|
||||||
|
public UnavailableTime(double probability, Distribution distribution) {
|
||||||
|
this.probability = probability;
|
||||||
|
this.distribution = distribution;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double sample(Rng rng) {
|
||||||
|
if (rng.random() < this.probability)
|
||||||
|
return Distribution.getPositiveSample(this.distribution, rng);
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package net.berack.upo.valpre.sim;
|
|||||||
*/
|
*/
|
||||||
public class Event implements Comparable<Event> {
|
public class Event implements Comparable<Event> {
|
||||||
public final double time;
|
public final double time;
|
||||||
|
public final double started;
|
||||||
public final Type type;
|
public final Type type;
|
||||||
public final ServerNode node;
|
public final ServerNode node;
|
||||||
|
|
||||||
@@ -15,10 +16,11 @@ public class Event implements Comparable<Event> {
|
|||||||
* @param node The node that the event is associated with.
|
* @param node The node that the event is associated with.
|
||||||
* @param time The time at which the event occurs.
|
* @param time The time at which the event occurs.
|
||||||
*/
|
*/
|
||||||
private Event(Type type, ServerNode node, double time) {
|
private Event(Type type, ServerNode node, double now, double time) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.time = time;
|
this.time = time;
|
||||||
this.node = node;
|
this.node = node;
|
||||||
|
this.started = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -30,39 +32,40 @@ public class Event implements Comparable<Event> {
|
|||||||
return 1;
|
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.
|
* Create a new arrival event.
|
||||||
*
|
*
|
||||||
* @param node The node that the event is associated with.
|
* @param node The node that the event is associated with.
|
||||||
|
* @param now The time at which the event has been created.
|
||||||
* @param time The time at which the event occurs.
|
* @param time The time at which the event occurs.
|
||||||
* @return The new event.
|
* @return The new event.
|
||||||
*/
|
*/
|
||||||
public static Event newArrival(ServerNode node, double time) {
|
public static Event newArrival(ServerNode node, double now, double time) {
|
||||||
return new Event(Type.ARRIVAL, node, time);
|
return new Event(Type.ARRIVAL, node, now, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new departure event.
|
* Create a new departure event.
|
||||||
*
|
*
|
||||||
* @param node The node that the event is associated with.
|
* @param node The node that the event is associated with.
|
||||||
|
* @param now The time at which the event has been created.
|
||||||
* @param time The time at which the event occurs.
|
* @param time The time at which the event occurs.
|
||||||
* @return The new event.
|
* @return The new event.
|
||||||
*/
|
*/
|
||||||
public static Event newDeparture(ServerNode node, double time) {
|
public static Event newDeparture(ServerNode node, double now, double time) {
|
||||||
return new Event(Type.DEPARTURE, node, time);
|
return new Event(Type.DEPARTURE, node, now, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new unavailable event.
|
||||||
|
*
|
||||||
|
* @param node The node that the event is associated with.
|
||||||
|
* @param now The time at which the event has been created.
|
||||||
|
* @param time The time at which the event occurs.
|
||||||
|
* @return The new event.
|
||||||
|
*/
|
||||||
|
public static Event newUnavailable(ServerNode node, double now, double time) {
|
||||||
|
return new Event(Type.UNAVAILABLE, node, now, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,5 +74,6 @@ public class Event implements Comparable<Event> {
|
|||||||
public static enum Type {
|
public static enum Type {
|
||||||
ARRIVAL,
|
ARRIVAL,
|
||||||
DEPARTURE,
|
DEPARTURE,
|
||||||
|
UNAVAILABLE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ public class ServerNode {
|
|||||||
public final String name;
|
public final String name;
|
||||||
public final int maxServers;
|
public final int maxServers;
|
||||||
public final int spawnArrivals;
|
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.
|
* Creates a source node with the given name and distribution.
|
||||||
@@ -23,7 +24,7 @@ public class ServerNode {
|
|||||||
* @return The created source node.
|
* @return The created source node.
|
||||||
*/
|
*/
|
||||||
public static ServerNode createSource(String name, Distribution distribution) {
|
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).
|
* arrivals to spawn that are served by infinite servers (Integer.MAX_VALUE).
|
||||||
*
|
*
|
||||||
* @param name The name of the node.
|
* @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.
|
* @param spawnArrivals The number of arrivals to spawn.
|
||||||
* @return The created source node.
|
* @return The created source node.
|
||||||
*/
|
*/
|
||||||
public static ServerNode createLimitedSource(String name, Distribution distribution, int spawnArrivals) {
|
public static ServerNode createLimitedSource(String name, Distribution service, int spawnArrivals) {
|
||||||
return new ServerNode(name, Integer.MAX_VALUE, distribution, spawnArrivals);
|
return new ServerNode(name, Integer.MAX_VALUE, service, null, spawnArrivals);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a queue node with the given name, maximum number of servers, and
|
* Creates a queue node with the given name, maximum number of servers, and
|
||||||
* distribution.
|
* distribution.
|
||||||
*
|
*
|
||||||
* @param name The name of the node.
|
* @param name The name of the node.
|
||||||
* @param maxServers The maximum number of servers in the queue.
|
* @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.
|
||||||
* @return The created queue node.
|
* @return The created queue node.
|
||||||
*/
|
*/
|
||||||
public static ServerNode createQueue(String name, int maxServers, Distribution distribution) {
|
public static ServerNode createQueue(String name, int maxServers, Distribution service) {
|
||||||
return new ServerNode(name, maxServers, distribution, 0);
|
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 name The name of the node.
|
||||||
* @param maxServers The maximum number of servers in the queue.
|
* @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.
|
* @param spawnArrivals The number of arrivals to spawn.
|
||||||
* @throws NullPointerException if the distribution is null
|
* @throws NullPointerException if the distribution is null
|
||||||
*/
|
*/
|
||||||
public ServerNode(String name, int maxServers, Distribution distribution, int spawnArrivals) {
|
private ServerNode(String name, int maxServers, Distribution service, Distribution unavailable, int spawnArrivals) {
|
||||||
if (distribution == null)
|
if (service == null)
|
||||||
throw new NullPointerException("Distribution can't be null");
|
throw new NullPointerException("Service distribution can't be null");
|
||||||
if (maxServers <= 0)
|
if (maxServers <= 0)
|
||||||
maxServers = 1;
|
maxServers = 1;
|
||||||
if (spawnArrivals < 0)
|
if (spawnArrivals < 0)
|
||||||
@@ -74,8 +90,9 @@ public class ServerNode {
|
|||||||
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.maxServers = maxServers;
|
this.maxServers = maxServers;
|
||||||
this.distribution = distribution;
|
|
||||||
this.spawnArrivals = spawnArrivals;
|
this.spawnArrivals = spawnArrivals;
|
||||||
|
this.service = service;
|
||||||
|
this.unavailable = unavailable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,12 +103,18 @@ public class ServerNode {
|
|||||||
* @param rng The random number generator to use.
|
* @param rng The random number generator to use.
|
||||||
* @return A positive sample from the distribution.
|
* @return A positive sample from the distribution.
|
||||||
*/
|
*/
|
||||||
public double getPositiveSample(Rng rng) {
|
public double getServiceTime(Rng rng) {
|
||||||
double sample;
|
return Distribution.getPositiveSample(this.service, rng);
|
||||||
do {
|
}
|
||||||
sample = this.distribution.sample(rng);
|
|
||||||
} while (sample < 0);
|
/**
|
||||||
return sample;
|
* 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);
|
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
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return this.name.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
|
* Creates a new run of the simulation with the given nodes and random number
|
||||||
* generator.
|
* generator.
|
||||||
*
|
*
|
||||||
* @param states The nodes in the network.
|
* @param states The nodes in the network.
|
||||||
* @param rng The random number generator to use.
|
* @param rng The random number generator to use.
|
||||||
* @param criterias when the simulation has to end.
|
* @param criterias when the simulation has to end.
|
||||||
*/
|
*/
|
||||||
@@ -75,21 +75,18 @@ public final class Simulation {
|
|||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case ARRIVAL -> {
|
case ARRIVAL -> {
|
||||||
state.queue.add(this.time);
|
state.queue.add(this.time);
|
||||||
state.stats.updateArrival(this.time, state.queue.size(), state.numServerBusy != 0);
|
state.stats.updateArrival(event, state.queue.size());
|
||||||
|
this.addDeparture(node, state);
|
||||||
if (state.numServerBusy < node.maxServers) {
|
|
||||||
state.numServerBusy++;
|
|
||||||
this.addDeparture(node);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case DEPARTURE -> {
|
case DEPARTURE -> {
|
||||||
var startService = state.queue.poll();
|
var arrivalTime = state.queue.poll();
|
||||||
state.stats.updateDeparture(this.time, this.time - startService);
|
state.stats.updateDeparture(event, arrivalTime);
|
||||||
|
state.numServerBusy--;
|
||||||
|
|
||||||
if (state.numServerBusy > state.queue.size()) {
|
if (this.addUnavailable(node)) {
|
||||||
state.numServerBusy--;
|
state.numServerUnavailable++;
|
||||||
} else {
|
} else {
|
||||||
this.addDeparture(node);
|
this.addDeparture(node, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
var next = this.net.getChildOf(node, this.rng);
|
var next = this.net.getChildOf(node, this.rng);
|
||||||
@@ -100,6 +97,11 @@ public final class Simulation {
|
|||||||
this.addArrival(node);
|
this.addArrival(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case UNAVAILABLE -> {
|
||||||
|
state.numServerUnavailable--;
|
||||||
|
state.stats.updateUnavailable(event);
|
||||||
|
this.addDeparture(node, state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,20 +145,42 @@ public final class Simulation {
|
|||||||
* @param node The node to create the event for.
|
* @param node The node to create the event for.
|
||||||
*/
|
*/
|
||||||
public void addArrival(ServerNode node) {
|
public void addArrival(ServerNode node) {
|
||||||
var event = Event.newArrival(node, this.time);
|
var event = Event.newArrival(node, this.time, this.time);
|
||||||
fel.add(event);
|
fel.add(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a departure event to the future event list. The event is created based
|
* 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.
|
* @param node The node to create the event for.
|
||||||
*/
|
*/
|
||||||
public void addDeparture(ServerNode node) {
|
public void addDeparture(ServerNode node, NodeState state) {
|
||||||
var delay = node.getPositiveSample(this.rng);
|
if (state.canServeRequest(node)) {
|
||||||
var event = Event.newDeparture(node, this.time + delay);
|
state.numServerBusy++;
|
||||||
fel.add(event);
|
var delay = node.getServiceTime(this.rng);
|
||||||
|
var event = Event.newDeparture(node, this.time, this.time + delay);
|
||||||
|
fel.add(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an unavailable event to the future event list if the delay is > 0.
|
||||||
|
* The event is created based on the given node, and the delay is determined by
|
||||||
|
* the node's unavailability distribution.
|
||||||
|
*
|
||||||
|
* @param node The node to create the event for.
|
||||||
|
* @return if the event has been added to the FEL.
|
||||||
|
*/
|
||||||
|
public boolean addUnavailable(ServerNode node) {
|
||||||
|
var delay = node.getUnavailableTime(rng);
|
||||||
|
if (delay > 0) {
|
||||||
|
var event = Event.newUnavailable(node, this.time, this.time + delay);
|
||||||
|
fel.add(event);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -183,7 +207,21 @@ public final class Simulation {
|
|||||||
*/
|
*/
|
||||||
public static class NodeState {
|
public static class NodeState {
|
||||||
public int numServerBusy = 0;
|
public int numServerBusy = 0;
|
||||||
|
public int numServerUnavailable = 0;
|
||||||
public final Statistics stats = new Statistics();
|
public final Statistics stats = new Statistics();
|
||||||
private final ArrayDeque<Double> queue = new ArrayDeque<>();
|
public final ArrayDeque<Double> queue = new ArrayDeque<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
*
|
||||||
|
* @param node
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean canServeRequest(ServerNode node) {
|
||||||
|
var totalOccupied = this.numServerBusy + this.numServerUnavailable;
|
||||||
|
var canServe = node.maxServers > totalOccupied;
|
||||||
|
var hasRequests = this.numServerBusy < this.queue.size();
|
||||||
|
return canServe && hasRequests;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ public class Result {
|
|||||||
this.simulationTime = time;
|
this.simulationTime = time;
|
||||||
this.timeElapsedMS = elapsed;
|
this.timeElapsedMS = elapsed;
|
||||||
this.nodes = nodes;
|
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.iFormat = "%" + this.size + ".0f";
|
||||||
this.fFormat = "%" + (this.size + 4) + ".3f";
|
this.fFormat = "%" + (this.size + 4) + ".3f";
|
||||||
}
|
}
|
||||||
@@ -56,8 +56,8 @@ public class Result {
|
|||||||
* the statistics for each node in the network.
|
* the statistics for each node in the network.
|
||||||
*/
|
*/
|
||||||
public String getSummary() {
|
public String getSummary() {
|
||||||
String[] h = { "Node", "Departures", "Avg Queue", "Avg Wait", "Avg Response", "Throughput", "Utilization %",
|
String[] h = { "Node", "Departures", "Avg Queue", "Avg Wait", "Avg Unavailable", "Avg Response", "Throughput",
|
||||||
"Last Event" };
|
"Utilization %", "Last Event" };
|
||||||
var table = new ConsoleTable(h);
|
var table = new ConsoleTable(h);
|
||||||
|
|
||||||
for (var entry : this.nodes.entrySet()) {
|
for (var entry : this.nodes.entrySet()) {
|
||||||
@@ -67,6 +67,7 @@ public class Result {
|
|||||||
iFormat.formatted(stats.numDepartures),
|
iFormat.formatted(stats.numDepartures),
|
||||||
fFormat.formatted(stats.avgQueueLength),
|
fFormat.formatted(stats.avgQueueLength),
|
||||||
fFormat.formatted(stats.avgWaitTime),
|
fFormat.formatted(stats.avgWaitTime),
|
||||||
|
fFormat.formatted(stats.avgUnavailableTime),
|
||||||
fFormat.formatted(stats.avgResponse),
|
fFormat.formatted(stats.avgResponse),
|
||||||
fFormat.formatted(stats.troughput),
|
fFormat.formatted(stats.troughput),
|
||||||
fFormat.formatted(stats.utilization * 100),
|
fFormat.formatted(stats.utilization * 100),
|
||||||
@@ -86,7 +87,7 @@ public class Result {
|
|||||||
|
|
||||||
if (tableHeader)
|
if (tableHeader)
|
||||||
builder.append(
|
builder.append(
|
||||||
"Seed,Node,Arrivals,Departures,MaxQueue,AvgQueue,AvgWait,AvgResponse,BusyTime,WaitTime,ResponseTime,LastEventTime,Throughput,Utilization\n");
|
"Seed,Node,Arrivals,Departures,MaxQueue,AvgQueue,AvgWait,AvgUnavailable,AvgResponse,BusyTime,WaitTime,UnavailableTime,ResponseTime,LastEventTime,Throughput,Utilization\n");
|
||||||
for (var entry : this.nodes.entrySet()) {
|
for (var entry : this.nodes.entrySet()) {
|
||||||
var stats = entry.getValue();
|
var stats = entry.getValue();
|
||||||
builder.append(this.seed);
|
builder.append(this.seed);
|
||||||
@@ -103,12 +104,16 @@ public class Result {
|
|||||||
builder.append(',');
|
builder.append(',');
|
||||||
builder.append(stats.avgWaitTime);
|
builder.append(stats.avgWaitTime);
|
||||||
builder.append(',');
|
builder.append(',');
|
||||||
|
builder.append(stats.avgUnavailableTime);
|
||||||
|
builder.append(',');
|
||||||
builder.append(stats.avgResponse);
|
builder.append(stats.avgResponse);
|
||||||
builder.append(',');
|
builder.append(',');
|
||||||
builder.append(stats.busyTime);
|
builder.append(stats.busyTime);
|
||||||
builder.append(',');
|
builder.append(',');
|
||||||
builder.append(stats.waitTime);
|
builder.append(stats.waitTime);
|
||||||
builder.append(',');
|
builder.append(',');
|
||||||
|
builder.append(stats.unavailableTime);
|
||||||
|
builder.append(',');
|
||||||
builder.append(stats.responseTime);
|
builder.append(stats.responseTime);
|
||||||
builder.append(',');
|
builder.append(',');
|
||||||
builder.append(stats.lastEventTime);
|
builder.append(stats.lastEventTime);
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package net.berack.upo.valpre.sim.stats;
|
|||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import net.berack.upo.valpre.sim.Event;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO
|
* TODO
|
||||||
*/
|
*/
|
||||||
@@ -11,12 +13,14 @@ public class Statistics {
|
|||||||
public double numDepartures = 0.0d;
|
public double numDepartures = 0.0d;
|
||||||
public double maxQueueLength = 0.0d;
|
public double maxQueueLength = 0.0d;
|
||||||
public double avgQueueLength = 0.0d;
|
public double avgQueueLength = 0.0d;
|
||||||
|
public double unavailableTime = 0.0d;
|
||||||
public double busyTime = 0.0d;
|
public double busyTime = 0.0d;
|
||||||
public double waitTime = 0.0d;
|
public double waitTime = 0.0d;
|
||||||
public double responseTime = 0.0d;
|
public double responseTime = 0.0d;
|
||||||
public double lastEventTime = 0.0d;
|
public double lastEventTime = 0.0d;
|
||||||
|
|
||||||
// derived stats, you can calculate them even at the end
|
// derived stats, you can calculate them even at the end
|
||||||
|
public double avgUnavailableTime = 0.0d;
|
||||||
public double avgWaitTime = 0.0d;
|
public double avgWaitTime = 0.0d;
|
||||||
public double avgResponse = 0.0d;
|
public double avgResponse = 0.0d;
|
||||||
public double troughput = 0.0d;
|
public double troughput = 0.0d;
|
||||||
@@ -25,39 +29,49 @@ public class Statistics {
|
|||||||
/**
|
/**
|
||||||
* TODO
|
* TODO
|
||||||
*
|
*
|
||||||
* @param time
|
* @param event
|
||||||
* @param newQueueSize
|
* @param newQueueSize
|
||||||
* @param updateBusy
|
|
||||||
*/
|
*/
|
||||||
public void updateArrival(double time, double newQueueSize, boolean updateBusy) {
|
public void updateArrival(Event event, double newQueueSize) {
|
||||||
var total = this.avgQueueLength * this.numArrivals;
|
var total = this.avgQueueLength * this.numArrivals;
|
||||||
|
|
||||||
this.numArrivals++;
|
this.numArrivals++;
|
||||||
this.avgQueueLength = (total + newQueueSize) / this.numArrivals;
|
this.avgQueueLength = (total + newQueueSize) / this.numArrivals;
|
||||||
this.maxQueueLength = Math.max(this.maxQueueLength, newQueueSize);
|
this.maxQueueLength = Math.max(this.maxQueueLength, newQueueSize);
|
||||||
if (updateBusy)
|
|
||||||
this.busyTime += time - this.lastEventTime;
|
|
||||||
|
|
||||||
this.lastEventTime = time;
|
this.lastEventTime = event.time;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO
|
* TODO
|
||||||
*
|
*
|
||||||
* @param time
|
* @param event
|
||||||
* @param response
|
* @param response
|
||||||
*/
|
*/
|
||||||
public void updateDeparture(double time, double response) {
|
public void updateDeparture(Event event, double arrivalTime) {
|
||||||
this.numDepartures++;
|
this.numDepartures++;
|
||||||
this.responseTime += response;
|
this.responseTime += event.time - arrivalTime;
|
||||||
this.busyTime += time - this.lastEventTime;
|
this.busyTime += event.time - event.started;
|
||||||
this.lastEventTime = time;
|
|
||||||
this.waitTime = this.responseTime - this.busyTime;
|
this.waitTime = this.responseTime - this.busyTime;
|
||||||
|
|
||||||
this.avgWaitTime = this.waitTime / this.numDepartures;
|
this.avgWaitTime = this.waitTime / this.numDepartures;
|
||||||
this.avgResponse = this.responseTime / this.numDepartures;
|
this.avgResponse = this.responseTime / this.numDepartures;
|
||||||
this.troughput = this.numDepartures / this.lastEventTime;
|
this.troughput = this.numDepartures / event.time;
|
||||||
this.utilization = this.busyTime / this.lastEventTime;
|
this.utilization = this.busyTime / event.time;
|
||||||
|
|
||||||
|
this.lastEventTime = event.time;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
public void updateUnavailable(Event event) {
|
||||||
|
this.unavailableTime += event.time - event.started;
|
||||||
|
this.avgUnavailableTime = this.unavailableTime / event.time;
|
||||||
|
|
||||||
|
this.lastEventTime = event.time;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -106,11 +120,13 @@ public class Statistics {
|
|||||||
save.avgQueueLength = func.apply(val1.avgQueueLength, val2.avgQueueLength);
|
save.avgQueueLength = func.apply(val1.avgQueueLength, val2.avgQueueLength);
|
||||||
save.busyTime = func.apply(val1.busyTime, val2.busyTime);
|
save.busyTime = func.apply(val1.busyTime, val2.busyTime);
|
||||||
save.responseTime = func.apply(val1.responseTime, val2.responseTime);
|
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.waitTime = func.apply(val1.waitTime, val2.waitTime);
|
||||||
save.lastEventTime = func.apply(val1.lastEventTime, val2.lastEventTime);
|
save.lastEventTime = func.apply(val1.lastEventTime, val2.lastEventTime);
|
||||||
// derived stats
|
// derived stats
|
||||||
save.avgWaitTime = func.apply(val1.avgWaitTime, val2.avgWaitTime);
|
save.avgWaitTime = func.apply(val1.avgWaitTime, val2.avgWaitTime);
|
||||||
save.avgResponse = func.apply(val1.avgResponse, val2.avgResponse);
|
save.avgResponse = func.apply(val1.avgResponse, val2.avgResponse);
|
||||||
|
save.avgUnavailableTime = func.apply(val1.avgUnavailableTime, val2.avgUnavailableTime);
|
||||||
save.troughput = func.apply(val1.troughput, val2.troughput);
|
save.troughput = func.apply(val1.troughput, val2.troughput);
|
||||||
save.utilization = func.apply(val1.utilization, val2.utilization);
|
save.utilization = func.apply(val1.utilization, val2.utilization);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ public class TestSimulation {
|
|||||||
private static double DELTA = 0.0000001;
|
private static double DELTA = 0.0000001;
|
||||||
private static Rng rigged = new RiggedRng();
|
private static Rng rigged = new RiggedRng();
|
||||||
private static Distribution const0 = new Constant(0.0);
|
private static Distribution const0 = new Constant(0.0);
|
||||||
private static Distribution const1 = new Constant(0.0);
|
private static Distribution const1 = new Constant(1.0);
|
||||||
|
|
||||||
private final static class RiggedRng extends Rng {
|
private final static class RiggedRng extends Rng {
|
||||||
@Override
|
@Override
|
||||||
@@ -38,7 +38,7 @@ public class TestSimulation {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void serverNode() {
|
public void serverNode() {
|
||||||
var node = new ServerNode("Nodo", 0, const1, 0);
|
var node = ServerNode.createQueue("Nodo", 0, const1);
|
||||||
assertEquals("Nodo", node.name);
|
assertEquals("Nodo", node.name);
|
||||||
assertEquals(1, node.maxServers);
|
assertEquals(1, node.maxServers);
|
||||||
assertFalse(node.shouldSpawnArrival(0));
|
assertFalse(node.shouldSpawnArrival(0));
|
||||||
@@ -46,7 +46,7 @@ public class TestSimulation {
|
|||||||
assertFalse(node.shouldSpawnArrival(1000));
|
assertFalse(node.shouldSpawnArrival(1000));
|
||||||
assertFalse(node.shouldSpawnArrival(Integer.MAX_VALUE));
|
assertFalse(node.shouldSpawnArrival(Integer.MAX_VALUE));
|
||||||
assertFalse(node.shouldSpawnArrival(-1));
|
assertFalse(node.shouldSpawnArrival(-1));
|
||||||
assertEquals(1.0, node.getPositiveSample(null), DELTA);
|
assertEquals(1.0, node.getServiceTime(null), DELTA);
|
||||||
|
|
||||||
node = ServerNode.createQueue("Queue", 50, const1);
|
node = ServerNode.createQueue("Queue", 50, const1);
|
||||||
assertEquals("Queue", node.name);
|
assertEquals("Queue", node.name);
|
||||||
@@ -56,7 +56,7 @@ public class TestSimulation {
|
|||||||
assertFalse(node.shouldSpawnArrival(1000));
|
assertFalse(node.shouldSpawnArrival(1000));
|
||||||
assertFalse(node.shouldSpawnArrival(Integer.MAX_VALUE));
|
assertFalse(node.shouldSpawnArrival(Integer.MAX_VALUE));
|
||||||
assertFalse(node.shouldSpawnArrival(-1));
|
assertFalse(node.shouldSpawnArrival(-1));
|
||||||
assertEquals(1.0, node.getPositiveSample(null), DELTA);
|
assertEquals(1.0, node.getServiceTime(null), DELTA);
|
||||||
|
|
||||||
node = ServerNode.createSource("Source", const1);
|
node = ServerNode.createSource("Source", const1);
|
||||||
assertEquals("Source", node.name);
|
assertEquals("Source", node.name);
|
||||||
@@ -67,7 +67,7 @@ public class TestSimulation {
|
|||||||
assertTrue(node.shouldSpawnArrival(Integer.MAX_VALUE - 1));
|
assertTrue(node.shouldSpawnArrival(Integer.MAX_VALUE - 1));
|
||||||
assertFalse(node.shouldSpawnArrival(Integer.MAX_VALUE));
|
assertFalse(node.shouldSpawnArrival(Integer.MAX_VALUE));
|
||||||
assertTrue(node.shouldSpawnArrival(-1));
|
assertTrue(node.shouldSpawnArrival(-1));
|
||||||
assertEquals(1.0, node.getPositiveSample(null), DELTA);
|
assertEquals(1.0, node.getServiceTime(null), DELTA);
|
||||||
|
|
||||||
node = ServerNode.createLimitedSource("Source", const1, 50);
|
node = ServerNode.createLimitedSource("Source", const1, 50);
|
||||||
assertEquals("Source", node.name);
|
assertEquals("Source", node.name);
|
||||||
@@ -78,25 +78,28 @@ public class TestSimulation {
|
|||||||
assertFalse(node.shouldSpawnArrival(1000));
|
assertFalse(node.shouldSpawnArrival(1000));
|
||||||
assertFalse(node.shouldSpawnArrival(Integer.MAX_VALUE));
|
assertFalse(node.shouldSpawnArrival(Integer.MAX_VALUE));
|
||||||
assertTrue(node.shouldSpawnArrival(-1));
|
assertTrue(node.shouldSpawnArrival(-1));
|
||||||
assertEquals(1.0, node.getPositiveSample(null), DELTA);
|
assertEquals(1.0, node.getServiceTime(null), DELTA);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void event() {
|
public void event() {
|
||||||
var node = ServerNode.createSource("Source", const0);
|
var node = ServerNode.createSource("Source", const0);
|
||||||
var event = Event.newType(node, 0, Event.Type.ARRIVAL);
|
var event = Event.newUnavailable(node, 0, 1);
|
||||||
assertEquals(node, event.node);
|
assertEquals(node, event.node);
|
||||||
assertEquals(0.0, event.time, 0.000000000001);
|
assertEquals(0.0, event.started, 0.000000000001);
|
||||||
assertEquals(Event.Type.ARRIVAL, event.type);
|
assertEquals(1.0, event.time, 0.000000000001);
|
||||||
|
assertEquals(Event.Type.UNAVAILABLE, event.type);
|
||||||
|
|
||||||
var event2 = Event.newArrival(node, 1.0);
|
var event2 = Event.newArrival(node, 1.0, 5.0);
|
||||||
assertEquals(node, event2.node);
|
assertEquals(node, event2.node);
|
||||||
assertEquals(1.0, event2.time, 0.000000000001);
|
assertEquals(1.0, event2.started, 0.000000000001);
|
||||||
|
assertEquals(5.0, event2.time, 0.000000000001);
|
||||||
assertEquals(Event.Type.ARRIVAL, event2.type);
|
assertEquals(Event.Type.ARRIVAL, event2.type);
|
||||||
|
|
||||||
var event3 = Event.newDeparture(node, 5.0);
|
var event3 = Event.newDeparture(node, 7.0, 8.0);
|
||||||
assertEquals(node, event3.node);
|
assertEquals(node, event3.node);
|
||||||
assertEquals(5.0, event3.time, 0.000000000001);
|
assertEquals(7.0, event3.started, 0.000000000001);
|
||||||
|
assertEquals(8.0, event3.time, 0.000000000001);
|
||||||
assertEquals(Event.Type.DEPARTURE, event3.type);
|
assertEquals(Event.Type.DEPARTURE, event3.type);
|
||||||
|
|
||||||
assertEquals(0, event2.compareTo(event2));
|
assertEquals(0, event2.compareTo(event2));
|
||||||
|
|||||||
Reference in New Issue
Block a user