/*
* Replication Benchmarker
* https://github.com/score-team/replication-benchmarker/
* Copyright (C) 2013 LORIA / Inria / SCORE Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package jbenchmarker;
import crdt.CRDT;
import crdt.Factory;
import crdt.PreconditionException;
import crdt.simulator.CausalSimulator;
import crdt.simulator.Trace;
import crdt.simulator.random.NTrace;
import crdt.simulator.random.OperationProfile;
import crdt.simulator.random.ProgressTrace;
import crdt.simulator.random.RandomTrace;
import crdt.simulator.sizecalculator.MemSizeCalculator;
import crdt.simulator.sizecalculator.SizeCalculator;
import crdt.simulator.sizecalculator.StandardSizeCalculator;
import crdt.simulator.tracestorage.TraceFromFile;
import crdt.simulator.tracestorage.TraceFromJSONObjectFile;
import crdt.simulator.tracestorage.TraceFromXMLObjectFile;
import crdt.simulator.tracestorage.TraceJSonObjectWriter;
import crdt.simulator.tracestorage.TraceObjectWriter;
import crdt.simulator.tracestorage.TraceStore;
import crdt.simulator.tracestorage.TraceXMLObjectWriter;
import crdt.tree.orderedtree.renderer.SizeJSonStyleDoc;
import crdt.tree.orderedtree.renderer.SizeXMLDoc;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import static jbenchmarker.TreeSimulation.Serialization.Data;
import static jbenchmarker.TreeSimulation.TraceFormat.JSON;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
/**
*
* @author Stephane Martin <stephane@stephanemartin.fr>
*/
public abstract class SimulationBase {
protected int baseSerializ = 1;
protected int totalDuration = 0;
protected CmdLineParser parser;
protected TraceParam traceP = new SimulationBase.TraceParam(0.1, 5, 10, 5);
protected List<NTrace.RandomParameters> randomTrace = new LinkedList();
enum Serialization {
OverHead, Data, OverHeadSer, DataSer, JSON, XML
}
enum TraceFormat {
Bin, XML, JSON
}
/**
* Arg4J arguments
*/
@Option(name = "--avg", usage = "time average in res files (default 100)")
int base = 100;
@Option(name = "-S", usage = "kind of mem mesures format (default is overHead)")
Serialization serialization = Serialization.OverHead;
@Option(name = "-t", usage = "trace file used for experimentation", metaVar = "TraceFile")
protected File traceFile;
@Option(name = "-T", usage = "Select trace format (default is binary)", metaVar = "TraceFormat")
private jbenchmarker.TreeSimulation.TraceFormat traceFormat = jbenchmarker.TreeSimulation.TraceFormat.Bin;
@Option(name = "-r", usage = "Thresold multiplicator")
private int thresold = 2;
@Option(name = "-P", usage = "Period of serialisation (default=0, disabled)")
private int scale = 0;
@Option(name = "-e", usage = "Number of execution (default 1)")
private int nbExec = 1;
@Option(name = "-h", usage = "display this message")
boolean help = false;
@Option(name = "--pass", usage = "set passive replicas number (default is 0)")
int passiveReplicats = 0;
@Option(name = "-J", usage = "Ignore the first run")
boolean justInTime = false;
@Option(name = "-O", usage = "prefix of output file")
String prefixOutput = null;
@Option(name = "--expname", usage = "folder of experience")
File folder;
@Option(name = "-p", usage = "Show progresss bar")
boolean progressBar = false;
@Option(name = "-R", usage = "Set random trace param probability,delay,deviation,replica", metaVar = "prob,delay,deviation,replica")
private void setTraceParam(String param) throws CmdLineException {
traceP = new TraceParam(param, parser);
}
/*
* end of arguements
*/
public void printMessageafterHelp() {
}
final void help(int exit) {
parser.printUsage(System.out);
System.exit(exit);
}
public SimulationBase(String... arg) {
try {
this.parser = new CmdLineParser(this);
parser.parseArgument(arg);
if (help) {
help(0);
}
} catch (CmdLineException ex) {
System.err.println("Error in argument " + ex);
help(-1);
}
}
LinkedList<List<Double>> resultsTimesLoc = new LinkedList();
LinkedList<List<Double>> resultsTimesDist = new LinkedList();
LinkedList<List<Double>> resultsMem = new LinkedList();
LinkedList<List<Double>> resultsTimesView = new LinkedList();
abstract Factory<CRDT> getFactory();
public Trace getTrace() {
Trace trace = new NTrace(randomTrace);
if (progressBar) {
trace = new ProgressTrace(trace, totalDuration);
}
return trace;
}
public SizeCalculator getSizeCalculator() {
switch (serialization) {
case JSON:
return new SizeJSonStyleDoc();
case XML:
return new SizeXMLDoc();
case DataSer:
return new StandardSizeCalculator(false);
case OverHeadSer:
return new StandardSizeCalculator(true);
case Data:
return new MemSizeCalculator(false);
case OverHead:
default:
return new MemSizeCalculator(true);
}
}
/**
* experimentation function
*/
public void run() throws IOException, PreconditionException {
/**
* setup
*/
Factory<CRDT> rf = getFactory();
System.out.println("-" + getDefaultPrefix());
SizeCalculator size = getSizeCalculator();
/**
* Simulation starts.
*/
for (int ex = 0; ex < nbExec; ex++) {
System.out.println("execution : " + ex);
CausalSimulator cd;
cd = new CausalSimulator(rf, true, scale, size);
cd.setPassiveReplica(passiveReplicats);
cd.setDebugInformation(false);
//Trace trace;
if (traceFile != null && traceFile.exists()) {
System.out.println("-Trace From File : " + traceFile);
cd.setWriter(null);
cd.run(traceReader());
} else {
TraceStore writer = null;
if (traceFile != null) {
System.out.println("-Trace to File : " + traceFile);
writer = getTraceWriter();
cd.setWriter(writer);
}
//System.out.println(getTrace());
cd.run(getTrace());
if (writer != null) {
writer.close();
}
}
System.out.println("End of simulation");
/*
* Store the result
*/
if (!justInTime || ex > 0) {
resultsTimesDist.add(cd.getAvgPerRemoteMessage());
resultsTimesLoc.add(castDoubleList(cd.getGenerationTimes()));
resultsMem.add(castDoubleList(cd.getMemUsed()));
resultsTimesView.add(castDoubleList(cd.getViewTime()));
} else {
System.out.println("\nIt was for fun !");
}
}
}
protected Trace traceReader() throws IOException {
switch (traceFormat) {
default:
case Bin:
return new TraceFromFile(traceFile, true);
case XML:
return new TraceFromXMLObjectFile(traceFile, true);
case JSON:
return new TraceFromJSONObjectFile(traceFile, true);
}
}
protected TraceStore getTraceWriter() throws IOException {
switch (traceFormat) {
default:
case Bin:
return new TraceObjectWriter(traceFile);
case XML:
return new TraceXMLObjectWriter(traceFile);
case JSON:
return new TraceJSonObjectWriter(traceFile);
}
}
static public List<Double> castDoubleList(List<Long> list) {
LinkedList<Double> ret = new LinkedList();
for (long l : list) {
ret.add(new Double(l));
}
return ret;
}
abstract String getDefaultPrefix();
public void writeFiles() throws FileNotFoundException {
if (this.prefixOutput == null) {
prefixOutput = getDefaultPrefix();
}
if (folder != null) {
if (!folder.exists()) {
folder.mkdir();
}
if (folder.isDirectory()) {
prefixOutput = folder.getName() + "/" + prefixOutput;
} else {
prefixOutput = folder.getName() + "." + prefixOutput;
}
}
resultsTimesDist.add(computeAvg(resultsTimesDist));
resultsTimesLoc.add(computeAvg(resultsTimesLoc));
writeMapToFile(resultsTimesDist, prefixOutput + "-dist.data");
writeMapToFile(resultsTimesLoc, prefixOutput + "-loc.data");
writeListToFile(resultsTimesDist.getLast(), prefixOutput + "-dist.res", base, 1000);//1000 for micro second
writeListToFile(resultsTimesLoc.getLast(), prefixOutput + "-loc.res", base, 1000);
if (!resultsMem.isEmpty() && !resultsMem.get(0).isEmpty()) {
resultsMem.add(computeAvg(resultsMem));
resultsTimesView.add(computeAvg(resultsTimesView));
writeMapToFile(resultsTimesView, prefixOutput + "-view.data");
writeListToFile(resultsTimesView.getLast(), prefixOutput + "-view.res", baseSerializ, 1000);//1000 for micro second
writeMapToFile(resultsMem, prefixOutput + "-mem.data");
writeListToFile(resultsMem.getLast(), prefixOutput + "-mem.res", baseSerializ, 1);
}
}
/**
* Write file in matrix format
*
* @param m matrix
* @param filename file name
* @throws FileNotFoundException
*/
public static void writeMapToFile(List<List<Double>> m, String filename) throws FileNotFoundException {
PrintWriter out = new PrintWriter(filename);
Iterator[] iterators = getIterators(m);
while (hasNext(iterators)) {
for (int i = 0; i < iterators.length; i++) {
out.print(iterators[i].next());
out.print((i == iterators.length - 1) ? "\n" : "\t");
}
}
out.close();
}
/**
* Check each iterator has next.
*
* @param it
* @return
*/
static private boolean hasNext(Iterator[] it) {
int ok = 0;
for (int i = 0; i < it.length; i++) {
if (it[i].hasNext()) {
ok++;
}
}
return ok == it.length;
}
/**
* Returne a array of iterator
*
* @param matrix
* @return array of iterator
*/
static private Iterator<Double>[] getIterators(List<List<Double>> matrix) {
Iterator<Double>[] iterators = new Iterator[matrix.size()];
int i = 0;
for (List<Double> l : matrix) {
iterators[i++] = l.iterator();
}
return iterators;
}
/**
* Write file the list item which grouped by nbAvg and the result is divided
* by number div.
*
* @param list
* @param filename
* @param nbAvg
* @param div
* @throws FileNotFoundException
*/
public static void writeListToFile(List<Double> list, String filename, int nbAvg, int div) throws FileNotFoundException {
PrintWriter out = new PrintWriter(filename);
double moy = 0;
double nb = 0;
for (Double o : list) {
nb++;
moy += o;
if (nb == nbAvg) {
out.println(((moy / nb) / ((double) div)));
nb = 0;
moy = 0;
}
}
out.close();
}
/**
* Compute average for each indices in lists
*
* @param list
* @return the average list
*/
public List<Double> computeAvg(List<List<Double>> list) {
if (list.size() == 1) {
return list.get(0);
}
List<Double> ret = new LinkedList();
Iterator<Double>[] iterators = getIterators(list);
LinkedList<Double> vals = new LinkedList();
double moy = 0;
while (hasNext(iterators)) {
for (int i = 0; i < iterators.length; i++) {
Double value = iterators[i].next();
vals.add(value);
moy += value;
}
moy /= vals.size();
double moyfinal = 0;
int nbElem = 0;
for (Double l : vals) {
if (l < thresold * moy) {
nbElem++;
moyfinal += l;
}
}
vals.clear();
moyfinal /= (double) nbElem;
ret.add((double) moyfinal);
}
return ret;
}
protected static class TraceParam {
double probability;
long delay;
double sdv;
int replicas;
public double getProbability() {
return probability;
}
public long getDelay() {
return delay;
}
public double getSdv() {
return sdv;
}
public int getReplicas() {
return replicas;
}
public TraceParam(String str, CmdLineParser parser) throws CmdLineException {
try {
str = str.replace(")", "");
str = str.replace("(", "");
String param[] = str.split(",");
probability = Double.parseDouble(param[0]);
delay = Long.parseLong(param[1]);
sdv = Double.parseDouble(param[2]);
replicas = Integer.parseInt(param[3]);
} catch (Exception ex) {
throw new CmdLineException(parser, "Parameter is invalid " + ex);
}
}
public NTrace.RandomParameters makeRandomTrace(int duration, OperationProfile opprof) {
// System.out.println("setup: \n"+probability+" prob to gen op\n"+delay+" delay of op\n"+sdv+" of deviation\n"+replicas+" replicas");
return new NTrace.RandomParameters(duration, RandomTrace.FLAT, opprof, probability, delay, sdv, replicas);
}
public TraceParam(double probability, long delay, double sdv, int replicas) {
this.probability = probability;
this.delay = delay;
this.sdv = sdv;
this.replicas = replicas;
}
}
}