|
@@ -0,0 +1,293 @@
|
|
|
|
|
+package xdevs.core.examples.devstone;
|
|
|
|
|
+
|
|
|
|
|
+import java.io.BufferedWriter;
|
|
|
|
|
+import java.io.File;
|
|
|
|
|
+import java.io.FileWriter;
|
|
|
|
|
+import java.io.IOException;
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.Arrays;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.logging.Level;
|
|
|
|
|
+import java.util.logging.Logger;
|
|
|
|
|
+
|
|
|
|
|
+import xdevs.core.modeling.Coupled;
|
|
|
|
|
+import xdevs.core.simulation.Coordinator;
|
|
|
|
|
+import xdevs.core.util.DevsLogger;
|
|
|
|
|
+
|
|
|
|
|
+public class Main {
|
|
|
|
|
+ private static final Logger LOGGER = Logger.getLogger(Main.class.getName());
|
|
|
|
|
+
|
|
|
|
|
+ private static final int MAX_EVENTS = 1;
|
|
|
|
|
+ private static final double PREPARATION_TIME = 0.0;
|
|
|
|
|
+ private static final double PERIOD = 1;
|
|
|
|
|
+
|
|
|
|
|
+ public static enum BenchmarkType {
|
|
|
|
|
+ LI, HI, HO, HOmem, HOmod
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+- private String path = "../Results/Agents";
|
|
|
|
|
+ private String filename = "";
|
|
|
|
|
+ private boolean append = false;
|
|
|
|
|
+ private int maxDepth = 8;
|
|
|
|
|
+ private int depthStepsize = 0;
|
|
|
|
|
+ private int maxWidth = 8;
|
|
|
|
|
+ private int widthStepsize = 0;
|
|
|
|
|
+ private int repetitions = 1;
|
|
|
|
|
+ private boolean output = false;
|
|
|
|
|
+ private boolean stats = false;
|
|
|
|
|
+ private boolean profile = false; // Note: JVM profiling usually requires external agents
|
|
|
|
|
+ private boolean logging = false;
|
|
|
|
|
+ private List<String> modelStructure = new ArrayList<>(Arrays.asList("LI", "HI", "HO", "HOmod"));
|
|
|
|
|
+ private boolean memory = false;
|
|
|
|
|
+ private double interval = 1.0;
|
|
|
|
|
+
|
|
|
|
|
+ public static void main(String[] args) {
|
|
|
|
|
+ Main orchestrator = new Main();
|
|
|
|
|
+ orchestrator.parseArguments(args);
|
|
|
|
|
+ orchestrator.runOrchestrator();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void runOrchestrator() {
|
|
|
|
|
+ int ds = (depthStepsize > 0 && !memory) ? depthStepsize : maxDepth;
|
|
|
|
|
+ int ws = (widthStepsize > 0 && !memory) ? widthStepsize : maxWidth;
|
|
|
|
|
+ int reps = memory ? 1 : repetitions;
|
|
|
|
|
+
|
|
|
|
|
+ File timeDir = new File(path, "Time");
|
|
|
|
|
+ timeDir.mkdirs();
|
|
|
|
|
+
|
|
|
|
|
+ File memDir = new File(path, "Memory");
|
|
|
|
|
+ memDir.mkdirs();
|
|
|
|
|
+
|
|
|
|
|
+ for (String modelStr : modelStructure) {
|
|
|
|
|
+ BenchmarkType topologyType;
|
|
|
|
|
+ try {
|
|
|
|
|
+ topologyType = BenchmarkType.valueOf(modelStr);
|
|
|
|
|
+ } catch (IllegalArgumentException e) {
|
|
|
|
|
+ LOGGER.severe("Invalid model structure: " + modelStr);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String suffix = filename.isEmpty() ? "" : "_" + filename;
|
|
|
|
|
+
|
|
|
|
|
+ if (memory) {
|
|
|
|
|
+ String memFile = new File(memDir, modelStr + suffix + ".sqlite").getAbsolutePath();
|
|
|
|
|
+
|
|
|
|
|
+ if (!append && new File(memFile).exists()) {
|
|
|
|
|
+ new File(memFile).delete();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (output) {
|
|
|
|
|
+ System.out.println("Warming up JIT for " + modelStr + " (D:" + maxDepth + ", W:" + maxWidth + ")");
|
|
|
|
|
+ }
|
|
|
|
|
+ // Warmup run
|
|
|
|
|
+ runSingleSimulation(topologyType, maxDepth, maxWidth, false, false, false, "", interval);
|
|
|
|
|
+
|
|
|
|
|
+ System.out.println("Measuring memory (Timing results will not be saved)...");
|
|
|
|
|
+ double[] times = runSingleSimulation(topologyType, maxDepth, maxWidth, stats, logging, profile, memFile, interval);
|
|
|
|
|
+
|
|
|
|
|
+ if (output || stats) {
|
|
|
|
|
+ System.out.printf(" Creation: %.4fs | Setup: %.4fs | Sim: %.4fs\n", times[0], times[1], times[2]);
|
|
|
|
|
+ }
|
|
|
|
|
+ System.out.println("Saved memory database to " + memFile);
|
|
|
|
|
+ normalizeSimulationData(memFile); // Will need SQLite JDBC or system call
|
|
|
|
|
+
|
|
|
|
|
+ } else {
|
|
|
|
|
+ File timeFile = new File(timeDir, modelStr + suffix + ".csv");
|
|
|
|
|
+ boolean fileExists = timeFile.exists() && timeFile.length() > 0;
|
|
|
|
|
+
|
|
|
|
|
+ try (BufferedWriter writer = new BufferedWriter(new FileWriter(timeFile, append))) {
|
|
|
|
|
+ if (!append || !fileExists) {
|
|
|
|
|
+ writer.write("simulator,model_type,depth,width,run,creation_time,setup_time,simulation_time\n");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for (int depth = ds; depth <= maxDepth; depth += ds) {
|
|
|
|
|
+ for (int width = ws; width <= maxWidth; width += ws) {
|
|
|
|
|
+
|
|
|
|
|
+ if (output) {
|
|
|
|
|
+ System.out.println("Warming up JIT for " + modelStr + " (D:" + depth + ", W:" + width + ")");
|
|
|
|
|
+ }
|
|
|
|
|
+ // Warmup
|
|
|
|
|
+ runSingleSimulation(topologyType, depth, width, false, false, false, "", interval);
|
|
|
|
|
+
|
|
|
|
|
+ for (int run = 0; run < reps; run++) {
|
|
|
|
|
+ if (output || stats) {
|
|
|
|
|
+ System.out.println("Running " + modelStr + " (D:" + depth + ", W:" + width + ") - Repetition " + run + "...");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ double[] times = runSingleSimulation(topologyType, depth, width, stats, logging, profile, "", interval);
|
|
|
|
|
+
|
|
|
|
|
+ writer.write(String.format("java_xdevs,%s,%d,%d,%d,%f,%f,%f\n",
|
|
|
|
|
+ modelStr, depth, width, run, times[0], times[1], times[2]));
|
|
|
|
|
+ writer.flush();
|
|
|
|
|
+
|
|
|
|
|
+ if (output || stats) {
|
|
|
|
|
+ System.out.printf("\tCreation: %.4fs | Setup: %.4fs | Sim: %.4fs\n", times[0], times[1], times[2]);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ LOGGER.severe(e.getLocalizedMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ System.out.println("Saved timing results to " + timeFile.getAbsolutePath());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private double[] runSingleSimulation(BenchmarkType modelType, int depth, int width,
|
|
|
|
|
+ boolean stats, boolean logging, boolean profile,
|
|
|
|
|
+ String memoryDb, double interval) {
|
|
|
|
|
+ System.gc();
|
|
|
|
|
+
|
|
|
|
|
+ Process ppProc = null;
|
|
|
|
|
+ if (memoryDb != null && !memoryDb.isEmpty()) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ long myPid = ProcessHandle.current().pid();
|
|
|
|
|
+ String procpathQuery = "$..children[?(@.stat.pid == " + myPid + ")]";
|
|
|
|
|
+ ProcessBuilder pb = new ProcessBuilder(
|
|
|
|
|
+ "uv", "run", "-m", "procpath", "record",
|
|
|
|
|
+ "-i", String.valueOf(interval), "-f", "stat",
|
|
|
|
|
+ "-d", memoryDb, procpathQuery
|
|
|
|
|
+ );
|
|
|
|
|
+ ppProc = pb.start();
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ LOGGER.warning("Could not start procpath: " + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ long startModel = System.nanoTime();
|
|
|
|
|
+ Coupled framework = buildFramework(modelType, width, depth);
|
|
|
|
|
+ long endModel = System.nanoTime();
|
|
|
|
|
+
|
|
|
|
|
+ long startSetup = System.nanoTime();
|
|
|
|
|
+ Coordinator coordinator = new Coordinator(framework); // Simplified to standard coordinator
|
|
|
|
|
+ coordinator.initialize();
|
|
|
|
|
+ long endSetup = System.nanoTime();
|
|
|
|
|
+
|
|
|
|
|
+ long startSim = System.nanoTime();
|
|
|
|
|
+ coordinator.simulate(Long.MAX_VALUE);
|
|
|
|
|
+ coordinator.exit();
|
|
|
|
|
+ long endSim = System.nanoTime();
|
|
|
|
|
+
|
|
|
|
|
+ if (memoryDb != null && !memoryDb.isEmpty() && ppProc != null) {
|
|
|
|
|
+ ppProc.destroy(); // Base.kill(ppProc, 2) in Julia
|
|
|
|
|
+ try {
|
|
|
|
|
+ ppProc.waitFor();
|
|
|
|
|
+ } catch (InterruptedException e) {
|
|
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ double tCreation = (endModel - startModel) / 1e9;
|
|
|
|
|
+ double tSetup = (endSetup - startSetup) / 1e9;
|
|
|
|
|
+ double tSim = (endSim - startSim) / 1e9;
|
|
|
|
|
+
|
|
|
|
|
+ return new double[]{tCreation, tSetup, tSim};
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Coupled buildFramework(BenchmarkType modelType, int width, int depth) {
|
|
|
|
|
+ Coupled framework = new Coupled("DevStone" + modelType.toString());
|
|
|
|
|
+ DevStoneGenerator generator = new DevStoneGenerator("Generator", PREPARATION_TIME, PERIOD, MAX_EVENTS);
|
|
|
|
|
+ framework.addComponent(generator);
|
|
|
|
|
+
|
|
|
|
|
+ DevStone stone = null;
|
|
|
|
|
+ // Setting delays as 0.0 constant to match Julia benchmark parameters
|
|
|
|
|
+ switch (modelType) {
|
|
|
|
|
+ case LI:
|
|
|
|
|
+ stone = new DevStoneCoupledLI("C", width, depth, PREPARATION_TIME, 0.0, 0.0);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case HI:
|
|
|
|
|
+ stone = new DevStoneCoupledHI("C", width, depth, PREPARATION_TIME, 0.0, 0.0);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case HO:
|
|
|
|
|
+ stone = new DevStoneCoupledHO("C", width, depth, PREPARATION_TIME, 0.0, 0.0);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case HOmod:
|
|
|
|
|
+ stone = new DevStoneCoupledHOmod("C", width, depth, PREPARATION_TIME, 0.0, 0.0);
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ framework.addComponent(stone);
|
|
|
|
|
+ framework.addCoupling(generator.oOut, stone.iIn);
|
|
|
|
|
+
|
|
|
|
|
+ if (modelType == BenchmarkType.HO || modelType == BenchmarkType.HOmod || modelType == BenchmarkType.HOmem) {
|
|
|
|
|
+ if (stone instanceof DevStoneCoupledHO) framework.addCoupling(generator.oOut, ((DevStoneCoupledHO) stone).iInAux);
|
|
|
|
|
+ if (stone instanceof DevStoneCoupledHOmod) framework.addCoupling(generator.oOut, ((DevStoneCoupledHOmod) stone).iInAux);
|
|
|
|
|
+ }
|
|
|
|
|
+ return framework;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void normalizeSimulationData(String dbPath) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ System.out.println("Normalizing database. All processes now start at 0 (Net Growth).");
|
|
|
|
|
+ String[] cmd = {
|
|
|
|
|
+ "sqlite3", dbPath,
|
|
|
|
|
+ "ALTER TABLE record ADD COLUMN raw_rss INTEGER;" +
|
|
|
|
|
+ "UPDATE record SET raw_rss = stat_rss;" +
|
|
|
|
|
+ "UPDATE record SET stat_rss = raw_rss - (SELECT r2.raw_rss FROM record r2 WHERE r2.stat_pid = record.stat_pid ORDER BY r2.record_id ASC LIMIT 1);"
|
|
|
|
|
+ };
|
|
|
|
|
+ ProcessBuilder pb = new ProcessBuilder(cmd);
|
|
|
|
|
+ Process p = pb.start();
|
|
|
|
|
+ p.waitFor();
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ LOGGER.warning("Could not normalize sqlite database using CLI sqlite3: " + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void parseArguments(String[] args) {
|
|
|
|
|
+ for (int i = 0; i < args.length; i++) {
|
|
|
|
|
+ String arg = args[i];
|
|
|
|
|
+
|
|
|
|
|
+ // Handles both -flag value AND --flag=value formats
|
|
|
|
|
+ String value = "";
|
|
|
|
|
+ if (arg.contains("=")) {
|
|
|
|
|
+ String[] parts = arg.split("=", 2);
|
|
|
|
|
+ arg = parts[0];
|
|
|
|
|
+ value = parts[1];
|
|
|
|
|
+ } else if (i + 1 < args.length && !args[i+1].startsWith("-")) {
|
|
|
|
|
+ value = args[i+1];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ switch (arg) {
|
|
|
|
|
+ case "--path": case "-p":
|
|
|
|
|
+ this.path = value; break;
|
|
|
|
|
+ case "--filename": case "-f":
|
|
|
|
|
+ this.filename = value; break;
|
|
|
|
|
+ case "--append": case "-a":
|
|
|
|
|
+ this.append = true; break;
|
|
|
|
|
+ case "--max-depth": case "-D":
|
|
|
|
|
+ this.maxDepth = Integer.parseInt(value); break;
|
|
|
|
|
+ case "--depth-stepsize": case "--ds":
|
|
|
|
|
+ this.depthStepsize = Integer.parseInt(value); break;
|
|
|
|
|
+ case "--max-width": case "-W":
|
|
|
|
|
+ this.maxWidth = Integer.parseInt(value); break;
|
|
|
|
|
+ case "--width-stepsize": case "--ws":
|
|
|
|
|
+ this.widthStepsize = Integer.parseInt(value); break;
|
|
|
|
|
+ case "--repetitions": case "-r":
|
|
|
|
|
+ this.repetitions = Integer.parseInt(value); break;
|
|
|
|
|
+ case "--output": case "-O":
|
|
|
|
|
+ this.output = true; break;
|
|
|
|
|
+ case "--stats": case "-s":
|
|
|
|
|
+ this.stats = true; break;
|
|
|
|
|
+ case "--profile": case "-P":
|
|
|
|
|
+ this.profile = true; break;
|
|
|
|
|
+ case "--logging": case "-L":
|
|
|
|
|
+ this.logging = true; break;
|
|
|
|
|
+ case "--model-structure": case "--ms":
|
|
|
|
|
+ // Consume multiple arguments until the next flag
|
|
|
|
|
+ this.modelStructure.clear();
|
|
|
|
|
+ if (!value.isEmpty()) this.modelStructure.add(value);
|
|
|
|
|
+ while (i + 1 < args.length && !args[i+1].startsWith("-")) {
|
|
|
|
|
+ this.modelStructure.add(args[++i]);
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ case "--memory": case "-M":
|
|
|
|
|
+ this.memory = true; break;
|
|
|
|
|
+ case "--interval": case "-i":
|
|
|
|
|
+ this.interval = Double.parseDouble(value); break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|