Param
- added parameters to main - print and/or save - added more data to the stats - removed lower and upper bounds for simplier error
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -25,4 +25,5 @@ replay_pid*
|
||||
|
||||
|
||||
# mine
|
||||
pdf/*
|
||||
pdf/*
|
||||
*.csv
|
||||
36
.vscode/launch.json
vendored
Normal file
36
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Run1k Rand",
|
||||
"request": "launch",
|
||||
"mainClass": "net.berack.upo.valpre.Main",
|
||||
"args": "-runs 1000 -p -seed 0"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Run1k save",
|
||||
"request": "launch",
|
||||
"mainClass": "net.berack.upo.valpre.Main",
|
||||
"args": "-runs 1000 -p -csv result.csv"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Run1k",
|
||||
"request": "launch",
|
||||
"mainClass": "net.berack.upo.valpre.Main",
|
||||
"args": "-runs 1000 -p"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Run10",
|
||||
"request": "launch",
|
||||
"mainClass": "net.berack.upo.valpre.Main",
|
||||
"args": "-runs 10"
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package net.berack.upo.valpre;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.berack.upo.valpre.rand.Distribution;
|
||||
import net.berack.upo.valpre.sim.SimulationMultiple;
|
||||
import net.berack.upo.valpre.sim.Net;
|
||||
@@ -8,12 +11,24 @@ import net.berack.upo.valpre.sim.ServerNode;
|
||||
public class Main {
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Parameters for the simulation
|
||||
var seed = 2007539552;
|
||||
String csv = null;
|
||||
var runs = 100;
|
||||
var seed = 2007539552L;
|
||||
var total = 10000;
|
||||
var lambda = 1.0 / 4.5;
|
||||
var mu = 3.2;
|
||||
var sigma = 0.6;
|
||||
|
||||
// Evantually change the parameters
|
||||
var arguments = parseParameters(args);
|
||||
if (arguments.containsKey("seed"))
|
||||
seed = Long.parseLong(arguments.get("seed"));
|
||||
if (arguments.containsKey("runs"))
|
||||
runs = Integer.parseInt(arguments.get("runs"));
|
||||
if (arguments.containsKey("csv"))
|
||||
csv = arguments.get("csv");
|
||||
var parallel = arguments.containsKey("p");
|
||||
|
||||
// Build the network
|
||||
var net = new Net();
|
||||
var node1 = ServerNode.createLimitedSource("Source", new Distribution.Exponential(lambda), total);
|
||||
@@ -28,11 +43,39 @@ public class Main {
|
||||
// var maxTime = new EndSimulationCriteria.MaxTime(1000.0);
|
||||
var nano = System.nanoTime();
|
||||
var sim = new SimulationMultiple(net);
|
||||
var results = sim.runParallel(seed, 1000);
|
||||
var results = parallel ? sim.runParallel(seed, runs) : sim.run(seed, runs);
|
||||
nano = System.nanoTime() - nano;
|
||||
|
||||
System.out.print(results.average.getHeader());
|
||||
System.out.print(results.average.getSummary());
|
||||
System.out.println("Final time " + nano / 1e6 + "ms");
|
||||
|
||||
if (csv != null) {
|
||||
results.saveCSV(csv);
|
||||
System.out.println("Data saved to " + csv);
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, String> parseParameters(String[] args) {
|
||||
var arguments = new HashMap<String, Boolean>();
|
||||
arguments.put("p", false);
|
||||
arguments.put("seed", true);
|
||||
arguments.put("runs", true);
|
||||
arguments.put("csv", true);
|
||||
|
||||
var param = new Parameters("-", arguments);
|
||||
try {
|
||||
return param.parse(args);
|
||||
} catch (IllegalArgumentException e) {
|
||||
var descriptions = new HashMap<String, String>();
|
||||
descriptions.put("p", "Add this if you want the simulation to use threads (one each run)");
|
||||
descriptions.put("seed", "The seed of the simulation");
|
||||
descriptions.put("runs", "How many runs the simulator should run");
|
||||
descriptions.put("csv", "The filename for saving every run statistics");
|
||||
|
||||
System.out.println(e.getMessage());
|
||||
System.out.println(param.helper(descriptions));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
115
src/main/java/net/berack/upo/valpre/Parameters.java
Normal file
115
src/main/java/net/berack/upo/valpre/Parameters.java
Normal file
@@ -0,0 +1,115 @@
|
||||
package net.berack.upo.valpre;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
public class Parameters {
|
||||
private final Map<String, Boolean> arguments;
|
||||
private final String prefix;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @param arguments
|
||||
*/
|
||||
public Parameters(String prefix, Map<String, Boolean> arguments) {
|
||||
if (arguments == null || arguments.size() == 0)
|
||||
throw new IllegalArgumentException();
|
||||
this.arguments = arguments;
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @param eventualDescription
|
||||
* @return
|
||||
*/
|
||||
public String helper(Map<String, String> eventualDescription) {
|
||||
var size = 0;
|
||||
var parameters = new HashMap<String, String>();
|
||||
|
||||
for (var param : this.arguments.entrySet()) {
|
||||
var string = this.prefix + param.getKey();
|
||||
if (param.getValue())
|
||||
string += " <value>";
|
||||
|
||||
parameters.put(param.getKey(), string);
|
||||
size = Math.max(size, string.length());
|
||||
}
|
||||
size += 2; // spacing
|
||||
|
||||
var builder = new StringBuilder();
|
||||
for (var param : parameters.entrySet()) {
|
||||
var key = param.getKey();
|
||||
var args = param.getValue();
|
||||
|
||||
builder.append(" ");
|
||||
builder.append(args);
|
||||
|
||||
var desc = eventualDescription.get(key);
|
||||
if (desc != null) {
|
||||
builder.append(" ".repeat(size - args.length()));
|
||||
builder.append(desc);
|
||||
}
|
||||
builder.append("\n");
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @param args
|
||||
* @return
|
||||
*/
|
||||
public Map<String, String> parse(String[] args) {
|
||||
var result = new HashMap<String, String>();
|
||||
if (args == null || args.length == 0)
|
||||
return result;
|
||||
|
||||
for (var i = 0; i < args.length; i += 1) {
|
||||
var current = args[i];
|
||||
var next = i + 1 < args.length ? args[i + 1] : null;
|
||||
|
||||
var updateI = this.parseSingle(current, next, result);
|
||||
if (updateI)
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @param current
|
||||
* @param next
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private boolean parseSingle(String current, String next, Map<String, String> result) {
|
||||
if (!current.startsWith(this.prefix))
|
||||
throw new IllegalArgumentException("Missing prefix [" + current + "]");
|
||||
current = current.substring(this.prefix.length());
|
||||
|
||||
var value = this.arguments.get(current);
|
||||
if (value != null) {
|
||||
result.put(current, value ? next : "");
|
||||
return value;
|
||||
}
|
||||
|
||||
var finalSize = result.size() + current.length();
|
||||
for (var letter : current.split(""))
|
||||
if (this.arguments.get(letter) != null)
|
||||
result.put(current, "");
|
||||
|
||||
if (finalSize != result.size())
|
||||
throw new IllegalArgumentException("Argument unknown");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,9 @@ public class Result {
|
||||
public final long seed;
|
||||
public final double simulationTime;
|
||||
public final double timeElapsedMS;
|
||||
private int size;
|
||||
private String iFormat;
|
||||
private String fFormat;
|
||||
|
||||
/**
|
||||
* Creates a new result object for the given parameters obtained by the
|
||||
@@ -28,6 +31,9 @@ public class Result {
|
||||
this.simulationTime = time;
|
||||
this.timeElapsedMS = elapsed;
|
||||
this.nodes = nodes;
|
||||
this.size = (int) Math.ceil(Math.log10(this.simulationTime));
|
||||
this.iFormat = "%" + this.size + ".0f";
|
||||
this.fFormat = "%" + (this.size + 4) + ".3f";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,13 +42,11 @@ public class Result {
|
||||
* real time
|
||||
*/
|
||||
public String getHeader() {
|
||||
var size = (int) Math.ceil(Math.log10(this.simulationTime));
|
||||
var format = "%" + (size + 4) + ".3f";
|
||||
var builder = new StringBuilder();
|
||||
builder.append("===== Net Stats =====\n");
|
||||
builder.append(String.format("Seed: \t%d\n", this.seed));
|
||||
builder.append(String.format("Simulation: \t" + format + "\n", this.simulationTime));
|
||||
builder.append(String.format("Elapsed: \t" + format + "ms\n", this.timeElapsedMS / 1e6));
|
||||
builder.append(String.format("Simulation: \t" + fFormat + "\n", this.simulationTime));
|
||||
builder.append(String.format("Elapsed: \t" + fFormat + "ms\n", this.timeElapsedMS / 1e6));
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@@ -52,24 +56,68 @@ public class Result {
|
||||
* the statistics for each node in the network.
|
||||
*/
|
||||
public String getSummary() {
|
||||
var size = (int) Math.ceil(Math.log10(this.simulationTime));
|
||||
var iFormat = "%" + size + ".0f";
|
||||
var fFormat = "%" + (size + 4) + ".3f";
|
||||
|
||||
String[] h = { "Node", "Departures", "Avg Queue", "Avg Response", "Throughput", "Utilization %", "Last Event" };
|
||||
String[] h = { "Node", "Departures", "Avg Queue", "Avg Wait", "Avg Response", "Throughput", "Utilization %",
|
||||
"Last Event" };
|
||||
var table = new ConsoleTable(h);
|
||||
|
||||
for (var entry : this.nodes.entrySet()) {
|
||||
var stats = entry.getValue();
|
||||
table.addRow(
|
||||
entry.getKey(),
|
||||
String.format(iFormat, stats.numDepartures),
|
||||
String.format(fFormat, stats.avgQueueLength),
|
||||
String.format(fFormat, stats.avgResponse),
|
||||
String.format(fFormat, stats.troughput),
|
||||
String.format(fFormat, stats.utilization * 100),
|
||||
String.format(fFormat, stats.lastEventTime));
|
||||
iFormat.formatted(stats.numDepartures),
|
||||
fFormat.formatted(stats.avgQueueLength),
|
||||
fFormat.formatted(stats.avgWaitTime),
|
||||
fFormat.formatted(stats.avgResponse),
|
||||
fFormat.formatted(stats.troughput),
|
||||
fFormat.formatted(stats.utilization * 100),
|
||||
fFormat.formatted(stats.lastEventTime));
|
||||
}
|
||||
return table.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @param tableHeader
|
||||
* @return
|
||||
*/
|
||||
public String getSummaryCSV(boolean tableHeader) {
|
||||
var builder = new StringBuilder();
|
||||
|
||||
if (tableHeader)
|
||||
builder.append(
|
||||
"Seed,Node,Arrivals,Departures,MaxQueue,AvgQueue,AvgWait,AvgResponse,BusyTime,WaitTime,ResponseTime,LastEventTime,Throughput,Utilization\n");
|
||||
for (var entry : this.nodes.entrySet()) {
|
||||
var stats = entry.getValue();
|
||||
builder.append(this.seed);
|
||||
builder.append(',');
|
||||
builder.append(entry.getKey().replace(',', ';').replace('"', '\''));
|
||||
builder.append(',');
|
||||
builder.append(stats.numArrivals);
|
||||
builder.append(',');
|
||||
builder.append(stats.numDepartures);
|
||||
builder.append(',');
|
||||
builder.append(stats.maxQueueLength);
|
||||
builder.append(',');
|
||||
builder.append(stats.avgQueueLength);
|
||||
builder.append(',');
|
||||
builder.append(stats.avgWaitTime);
|
||||
builder.append(',');
|
||||
builder.append(stats.avgResponse);
|
||||
builder.append(',');
|
||||
builder.append(stats.busyTime);
|
||||
builder.append(',');
|
||||
builder.append(stats.waitTime);
|
||||
builder.append(',');
|
||||
builder.append(stats.responseTime);
|
||||
builder.append(',');
|
||||
builder.append(stats.lastEventTime);
|
||||
builder.append(',');
|
||||
builder.append(stats.troughput);
|
||||
builder.append(',');
|
||||
builder.append(stats.utilization);
|
||||
builder.append('\n');
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package net.berack.upo.valpre.sim.stats;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
@@ -9,8 +11,7 @@ public class ResultMultiple {
|
||||
public final Result[] runs;
|
||||
public final Result average;
|
||||
public final Result variance;
|
||||
public final Result lowerBound;
|
||||
public final Result upperBound;
|
||||
public final Result error95;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
@@ -18,13 +19,31 @@ public class ResultMultiple {
|
||||
* @param runs
|
||||
*/
|
||||
public ResultMultiple(Result... runs) {
|
||||
if (runs == null || runs.length <= 1)
|
||||
throw new IllegalArgumentException("Sample size must be > 1");
|
||||
|
||||
this.runs = runs;
|
||||
this.average = ResultMultiple.calcAvg(runs);
|
||||
this.variance = ResultMultiple.calcVar(this.average, runs);
|
||||
this.error95 = calcError(this.average, this.variance, runs.length, 0.95);
|
||||
}
|
||||
|
||||
var temp = calcInterval(this.average, this.variance, runs.length, 0.95);
|
||||
this.lowerBound = temp[0];
|
||||
this.upperBound = temp[1];
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @param filename
|
||||
* @throws IOException
|
||||
*/
|
||||
public void saveCSV(String filename) throws IOException {
|
||||
try (var file = new FileWriter(filename)) {
|
||||
var first = true;
|
||||
var builder = new StringBuilder();
|
||||
for (var run : this.runs) {
|
||||
builder.append(run.getSummaryCSV(first));
|
||||
first = false;
|
||||
}
|
||||
file.write(builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,10 +117,7 @@ public class ResultMultiple {
|
||||
* @param alpha
|
||||
* @return
|
||||
*/
|
||||
public static Result[] calcInterval(Result avg, Result stdDev, int sampleSize, double alpha) {
|
||||
if (sampleSize <= 1)
|
||||
throw new IllegalArgumentException("Il numero di campioni deve essere maggiore di 1.");
|
||||
|
||||
public static Result calcError(Result avg, Result stdDev, int sampleSize, double alpha) {
|
||||
// Getting the correct values for the percentile
|
||||
var distr = new org.apache.commons.math3.distribution.TDistribution(sampleSize - 1);
|
||||
var percentile = distr.inverseCumulativeProbability(alpha);
|
||||
@@ -117,31 +133,6 @@ public class ResultMultiple {
|
||||
stat.merge(entry.getValue(), (_, val) -> percentile * (val / sqrtSample));
|
||||
error.nodes.put(entry.getKey(), stat);
|
||||
}
|
||||
|
||||
// Calculating the lower and the upper bound
|
||||
var lowerBound = new Result(avg.seed,
|
||||
avg.simulationTime - error.simulationTime,
|
||||
avg.timeElapsedMS - error.timeElapsedMS,
|
||||
new HashMap<>());
|
||||
var upperBound = new Result(avg.seed,
|
||||
avg.simulationTime + error.simulationTime,
|
||||
avg.timeElapsedMS + error.timeElapsedMS,
|
||||
new HashMap<>());
|
||||
error.nodes.entrySet().forEach(entry -> {
|
||||
var key = entry.getKey();
|
||||
var errStat = entry.getValue();
|
||||
|
||||
var avgStat = avg.nodes.get(key);
|
||||
var lower = new Statistics();
|
||||
var upper = new Statistics();
|
||||
|
||||
Statistics.apply(lower, avgStat, errStat, (a, e) -> a - e);
|
||||
Statistics.apply(upper, avgStat, errStat, (a, e) -> a + e);
|
||||
|
||||
lowerBound.nodes.put(key, lower);
|
||||
upperBound.nodes.put(key, lower);
|
||||
});
|
||||
|
||||
return new Result[] { lowerBound, upperBound };
|
||||
return error;
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,12 @@ public class Statistics {
|
||||
public double maxQueueLength = 0.0d;
|
||||
public double avgQueueLength = 0.0d;
|
||||
public double busyTime = 0.0d;
|
||||
public double waitTime = 0.0d;
|
||||
public double responseTime = 0.0d;
|
||||
public double lastEventTime = 0.0d;
|
||||
|
||||
// derived stats, you can calculate them even at the end
|
||||
public double avgWaitTime = 0.0d;
|
||||
public double avgResponse = 0.0d;
|
||||
public double troughput = 0.0d;
|
||||
public double utilization = 0.0d;
|
||||
@@ -50,7 +52,9 @@ public class Statistics {
|
||||
this.responseTime += response;
|
||||
this.busyTime += time - this.lastEventTime;
|
||||
this.lastEventTime = time;
|
||||
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;
|
||||
@@ -102,10 +106,12 @@ 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.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.troughput = func.apply(val1.troughput, val2.troughput);
|
||||
save.utilization = func.apply(val1.utilization, val2.utilization);
|
||||
save.avgResponse = func.apply(val1.avgResponse, val2.avgResponse);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user