From 9bcf970c5dd1079f87eaea95e6bfae22c1a79abe Mon Sep 17 00:00:00 2001 From: Baptiste Lesquoy Date: Mon, 23 Sep 2024 15:02:19 +0700 Subject: [PATCH 1/2] Removes static functions in GAMA.java and give an accessor to bufferingController instead - also normalizes method names and remove a few warnings - removes unused save method in csvsaver --- .../kernel/simulation/SimulationAgent.java | 10 +++-- .../core/metamodel/agent/AbstractAgent.java | 4 +- gama.core/src/gama/core/runtime/GAMA.java | 41 +++---------------- .../concurrent/BufferingController.java | 20 ++++----- gama.core/src/gama/gaml/operators/Files.java | 4 +- .../gaml/statements/BenchmarkStatement.java | 2 +- .../gama/gaml/statements/SaveStatement.java | 2 +- .../gama/gaml/statements/WriteStatement.java | 2 +- .../gama/gaml/statements/save/CSVSaver.java | 23 +---------- .../gama/gaml/statements/save/TextSaver.java | 2 +- .../headless/runtime/HeadlessApplication.java | 2 +- .../src/gama/ui/application/Application.java | 4 +- 12 files changed, 35 insertions(+), 81 deletions(-) diff --git a/gama.core/src/gama/core/kernel/simulation/SimulationAgent.java b/gama.core/src/gama/core/kernel/simulation/SimulationAgent.java index 8cbb6a0c3e..57719a4dfa 100644 --- a/gama.core/src/gama/core/kernel/simulation/SimulationAgent.java +++ b/gama.core/src/gama/core/kernel/simulation/SimulationAgent.java @@ -403,8 +403,8 @@ protected void postStep(final IScope scope) { executer.executeOneShotActions(); if (outputs != null) { outputs.step(this.getScope()); } ownClock.step(); - GAMA.flushSaveFileStep(this); - GAMA.flushWriteStep(this); + GAMA.getBufferingController().flushSaveFilesInCycle(this); + GAMA.getBufferingController().flushWriteInCycle(this); } @Override @@ -456,8 +456,8 @@ public void dispose() { if (externalInitsAndParameters != null) { externalInitsAndParameters.clear(); } //we make sure that all pending write operations are flushed - GAMA.flushSaveFilePerOwner(this); - GAMA.flushWritePerAgent(this); + GAMA.getBufferingController().flushSaveFilesOfAgent(this); + GAMA.getBufferingController().flushWriteOfAgent(this); GAMA.releaseScope(getScope()); // scope = null; super.dispose(); @@ -789,6 +789,7 @@ public String buildPostfix() { * @param iOutputManager * the new outputs */ + @SuppressWarnings("unchecked") public void setOutputs(final IOutputManager iOutputManager) { if (iOutputManager == null) return; // AD : condition removed for Issue #3748 @@ -968,6 +969,7 @@ public void executeAction(final IExecutable executable) { } + @SuppressWarnings("unchecked") @Override public void updateWith(final IScope scope, final ISerialisedAgent sa) { diff --git a/gama.core/src/gama/core/metamodel/agent/AbstractAgent.java b/gama.core/src/gama/core/metamodel/agent/AbstractAgent.java index 2c00c05923..0e87ef0360 100644 --- a/gama.core/src/gama/core/metamodel/agent/AbstractAgent.java +++ b/gama.core/src/gama/core/metamodel/agent/AbstractAgent.java @@ -129,8 +129,8 @@ public void dispose() { attributes.clear(); attributes = null; } - GAMA.flushSaveFilePerOwner(this); - GAMA.flushWritePerAgent(this); + GAMA.getBufferingController().flushSaveFilesOfAgent(this); + GAMA.getBufferingController().flushWriteOfAgent(this); } /** diff --git a/gama.core/src/gama/core/runtime/GAMA.java b/gama.core/src/gama/core/runtime/GAMA.java index 18f77e156e..70065afcc5 100644 --- a/gama.core/src/gama/core/runtime/GAMA.java +++ b/gama.core/src/gama/core/runtime/GAMA.java @@ -104,41 +104,6 @@ public class GAMA { // hqnghi: add several controllers to have multi-thread experiments private static final List controllers = new CopyOnWriteArrayList<>(); - private static final BufferingController bufferingController = new BufferingController(); - - public static boolean askWriteFile(final IScope scope, final File f, final CharSequence content, - final SaveOptions options) { - return bufferingController.askWriteFile(f.getAbsolutePath(), scope, content, options); - } - - public static boolean askWriteConsole(final IScope scope, final StringBuilder content, final GamaColor color, - final BufferingStrategies strategy) { - return bufferingController.askWriteConsole(scope, content, color, strategy); - } - - public static boolean fileWillBeWritten(final File f) { - return bufferingController.isFileWaitingToBeWritten(f); - } - - public static boolean flushSaveFilePerOwner(final AbstractAgent owner) { - return bufferingController.flushSaveFilesOfOwner(owner); - } - - public static boolean flushSaveFileStep(final SimulationAgent owner) { - return bufferingController.flushSaveFilesInCycle(owner); - } - - public static void flushWriteStep(final SimulationAgent owner) { - bufferingController.flushWriteInCycle(owner); - } - - public static void flushWritePerAgent(final AbstractAgent owner) { - bufferingController.flushWriteOfOwner(owner); - } - - public static void flushAllBuffers() { - bufferingController.flushAllBuffers(); - } /** * Gets the controllers. @@ -155,6 +120,12 @@ public static void flushAllBuffers() { public static IExperimentController getFrontmostController() { return controllers.isEmpty() ? null : controllers.get(0); } + + private static final BufferingController bufferingController = new BufferingController(); + + public static BufferingController getBufferingController() {return bufferingController;} + + /** * New control architecture diff --git a/gama.core/src/gama/core/runtime/concurrent/BufferingController.java b/gama.core/src/gama/core/runtime/concurrent/BufferingController.java index 0fe53f52b3..775cdd2800 100644 --- a/gama.core/src/gama/core/runtime/concurrent/BufferingController.java +++ b/gama.core/src/gama/core/runtime/concurrent/BufferingController.java @@ -303,7 +303,7 @@ protected static boolean directWriteFile(final String fileId, final CharSequence * @param map the map in which to look up * @return true if everything went well, false in case of error */ - protected static boolean flushAllFilesOfOwner(AbstractAgent owner, Map> map) { + protected static boolean flushAllFilesOfAgent(AbstractAgent owner, Map> map) { boolean success = true; for(var entry : map.entrySet()) { var writeTask = entry.getValue().get(owner); @@ -326,7 +326,7 @@ protected static boolean flushAllFilesOfOwner(AbstractAgent owner, Map> map) { + protected static void flushAllWriteOfAgent(final AbstractAgent owner, final Map> map) { var tasks = map.get(owner); if (tasks != null) { var scope = owner.getScope(); @@ -344,8 +344,8 @@ protected static void flushAllWriteOfOwner(final AbstractAgent owner, final Map< * @param owner the simulation or agent in which the save statements have been executed * @return true if everything went well, false in case of error */ - public synchronized boolean flushSaveFilesOfOwner(AbstractAgent owner) { - return flushAllFilesOfOwner(owner, fileBufferPerAgent); + public synchronized boolean flushSaveFilesOfAgent(AbstractAgent owner) { + return flushAllFilesOfAgent(owner, fileBufferPerAgent); } /** @@ -354,7 +354,7 @@ public synchronized boolean flushSaveFilesOfOwner(AbstractAgent owner) { * @return true if everything went well, false in case of error */ public synchronized boolean flushSaveFilesInCycle(AbstractAgent owner) { - return flushAllFilesOfOwner(owner, fileBufferPerAgentForCycles); + return flushAllFilesOfAgent(owner, fileBufferPerAgentForCycles); } /** @@ -362,15 +362,15 @@ public synchronized boolean flushSaveFilesInCycle(AbstractAgent owner) { * @param owner the simulation in which the write statements have been executed */ public synchronized void flushWriteInCycle(AbstractAgent owner) { - flushAllWriteOfOwner(owner, consoleBufferListPerAgentForCycles); + flushAllWriteOfAgent(owner, consoleBufferListPerAgentForCycles); } /** * Flushes all the write requests made by an agent with the 'per_simulation' or 'per_agent' strategy * @param owner: the agent or simulation in which the write statements have been executed */ - public synchronized void flushWriteOfOwner(AbstractAgent owner) { - flushAllWriteOfOwner(owner, consoleBufferListPerAgent); + public synchronized void flushWriteOfAgent(AbstractAgent owner) { + flushAllWriteOfAgent(owner, consoleBufferListPerAgent); } /** @@ -384,7 +384,7 @@ public synchronized void flushAllBuffers() { } // flushes the others for the console for (var agent : consoleBufferListPerAgent.keySet()) { - flushWriteOfOwner(agent); + flushWriteOfAgent(agent); } // flushes the files registered for the cycle var agents = fileBufferPerAgentForCycles.entrySet().stream() @@ -400,7 +400,7 @@ public synchronized void flushAllBuffers() { .flatMap(Collection::stream) .toArray(length -> new AbstractAgent[length]); for (AbstractAgent agent : agents) { - flushSaveFilesOfOwner(agent); + flushSaveFilesOfAgent(agent); } } diff --git a/gama.core/src/gama/gaml/operators/Files.java b/gama.core/src/gama/gaml/operators/Files.java index c2db6dca1c..924daa4743 100644 --- a/gama.core/src/gama/gaml/operators/Files.java +++ b/gama.core/src/gama/gaml/operators/Files.java @@ -750,8 +750,8 @@ public static IGamaFile newFolder(final IScope scope, final String folder) throw @example ("full_all_files(simulation); // simulation is the current simulation, this can be important to differentiate in case of multi-simulation experiments")}, see = { "save"}) public static boolean flushAllFiles(final IScope scope, final SimulationAgent simulation) throws GamaRuntimeException { - boolean success = GAMA.flushSaveFileStep(simulation); - success &= GAMA.flushSaveFilePerOwner(simulation); + boolean success = GAMA.getBufferingController().flushSaveFilesInCycle(simulation); + success &= GAMA.getBufferingController().flushSaveFilesOfAgent(simulation); return success; } diff --git a/gama.core/src/gama/gaml/statements/BenchmarkStatement.java b/gama.core/src/gama/gaml/statements/BenchmarkStatement.java index 0955073601..b735058bb4 100644 --- a/gama.core/src/gama/gaml/statements/BenchmarkStatement.java +++ b/gama.core/src/gama/gaml/statements/BenchmarkStatement.java @@ -164,7 +164,7 @@ public Object privateExecuteIn(final IScope scope) throws GamaRuntimeException { else { messageToSend.append(Strings.LN); } - GAMA.askWriteConsole(scope, messageToSend, rgb, strategy); + GAMA.getBufferingController().askWriteConsole(scope, messageToSend, rgb, strategy); // scope.getGui().getConsole().informConsole(result, scope.getRoot(), null); return messageToSend.toString(); } diff --git a/gama.core/src/gama/gaml/statements/SaveStatement.java b/gama.core/src/gama/gaml/statements/SaveStatement.java index 7031af25f0..d0287344ef 100644 --- a/gama.core/src/gama/gaml/statements/SaveStatement.java +++ b/gama.core/src/gama/gaml/statements/SaveStatement.java @@ -468,7 +468,7 @@ public Object privateExecuteIn(final IScope scope) throws GamaRuntimeException { try { Files.createDirectories(fileToSave.toPath().getParent()); - boolean exists = fileToSave.exists() || GAMA.fileWillBeWritten(fileToSave); + boolean exists = fileToSave.exists() || GAMA.getBufferingController().isFileWaitingToBeWritten(fileToSave); final boolean rewrite = shouldOverwrite(scope); IExpression header = getFacet(IKeyword.HEADER); diff --git a/gama.core/src/gama/gaml/statements/WriteStatement.java b/gama.core/src/gama/gaml/statements/WriteStatement.java index 82375b0145..6c4dbd5490 100644 --- a/gama.core/src/gama/gaml/statements/WriteStatement.java +++ b/gama.core/src/gama/gaml/statements/WriteStatement.java @@ -177,7 +177,7 @@ public Object privateExecuteIn(final IScope scope) throws GamaRuntimeException { // DEBUG.OUT( // "" + getName() + " asking to write and passing " + scope.getRoot() + " as the corresponding agent"); - GAMA.askWriteConsole(scope, messageToSend, rgb, strategy); + GAMA.getBufferingController().askWriteConsole(scope, messageToSend, rgb, strategy); } return mes; } diff --git a/gama.core/src/gama/gaml/statements/save/CSVSaver.java b/gama.core/src/gama/gaml/statements/save/CSVSaver.java index 5dd52eeac6..9cc9f04862 100644 --- a/gama.core/src/gama/gaml/statements/save/CSVSaver.java +++ b/gama.core/src/gama/gaml/statements/save/CSVSaver.java @@ -18,7 +18,6 @@ import gama.core.metamodel.agent.IAgent; import gama.core.runtime.GAMA; import gama.core.runtime.IScope; -import gama.core.runtime.concurrent.BufferingController.BufferingStrategies; import gama.core.runtime.exceptions.GamaRuntimeException; import gama.core.util.GamaListFactory; import gama.core.util.IList; @@ -124,7 +123,7 @@ public void save(final IScope scope, final IExpression item, final File file, fi sb.append(Strings.LN); } if (itemType.id() == IType.MATRIX) { - GamaMatrix matrix = (GamaMatrix) value; + GamaMatrix matrix = (GamaMatrix) value; matrix.rowByRow(scope, v -> sb.append(toCleanString(v)), () -> sb.append(del), () -> sb.append(Strings.LN)); } else { @@ -136,27 +135,9 @@ public void save(final IScope scope, final IExpression item, final File file, fi } sb.append(Strings.LN); } - GAMA.askWriteFile(scope, file, sb, saveOptions); + GAMA.getBufferingController().askWriteFile(file.getAbsolutePath(), scope, sb, saveOptions); } - /** - * Save. - * - * @param scope - * the scope - * @param fw - * the fw - * @param header - * the header - * @param item - * the item - * @throws GamaRuntimeException - * the gama runtime exception - */ - private void save(final IScope scope, final File file, final boolean header, final IExpression item, final BufferingStrategies bufferingStrategy) - throws GamaRuntimeException { - - } /** * To clean string. diff --git a/gama.core/src/gama/gaml/statements/save/TextSaver.java b/gama.core/src/gama/gaml/statements/save/TextSaver.java index d227f72582..63330ac134 100644 --- a/gama.core/src/gama/gaml/statements/save/TextSaver.java +++ b/gama.core/src/gama/gaml/statements/save/TextSaver.java @@ -64,7 +64,7 @@ public void save(final IScope scope, final IExpression item, final File file, fi options.setCharSet(ch); try { - GAMA.askWriteFile(scope, file, toSave, options); + GAMA.getBufferingController().askWriteFile(file.getAbsolutePath(), scope, toSave, options); } catch (final GamaRuntimeException e) { throw e; } catch (final Exception e) { diff --git a/gama.headless/src/gama/headless/runtime/HeadlessApplication.java b/gama.headless/src/gama/headless/runtime/HeadlessApplication.java index 83b361c0c0..a63e0d308b 100644 --- a/gama.headless/src/gama/headless/runtime/HeadlessApplication.java +++ b/gama.headless/src/gama/headless/runtime/HeadlessApplication.java @@ -361,7 +361,7 @@ private static boolean showError(final int errorCode, final String path) { * the exception * @date 17 oct. 2023 */ - @SuppressWarnings ("unused") + @SuppressWarnings ({ "unchecked" }) @Override public Object start(final IApplicationContext context) throws Exception { diff --git a/gama.ui.application/src/gama/ui/application/Application.java b/gama.ui.application/src/gama/ui/application/Application.java index 456d11b08e..a55c567ff6 100644 --- a/gama.ui.application/src/gama/ui/application/Application.java +++ b/gama.ui.application/src/gama/ui/application/Application.java @@ -154,7 +154,7 @@ public Object start(final IApplicationContext context) throws Exception { if (display != null) { display.dispose(); } final Location instanceLoc = Platform.getInstanceLocation(); if (instanceLoc != null) { instanceLoc.release(); } - GAMA.flushAllBuffers(); + GAMA.getBufferingController().flushAllBuffers(); } } return EXIT_OK; @@ -271,7 +271,7 @@ && openQuestion(null, "Different version of the models library", @Override public void stop() { - GAMA.flushAllBuffers(); + GAMA.getBufferingController().flushAllBuffers(); final IWorkbench workbench = getWorkbench(); if (workbench == null) return; final Display display = workbench.getDisplay(); From 9f286fd7ffec9325e98a8caedf83b302f64110ae Mon Sep 17 00:00:00 2001 From: Baptiste Lesquoy Date: Mon, 23 Sep 2024 15:11:53 +0700 Subject: [PATCH 2/2] Adds models to illustrate buffering strategies --- .../System/Buffering execution order.gaml | 43 ++++++++++++++++ .../System/Buffering performances.gaml | 51 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 gama.library/models/GAML Syntax/System/Buffering execution order.gaml create mode 100644 gama.library/models/GAML Syntax/System/Buffering performances.gaml diff --git a/gama.library/models/GAML Syntax/System/Buffering execution order.gaml b/gama.library/models/GAML Syntax/System/Buffering execution order.gaml new file mode 100644 index 0000000000..65c0bf3cce --- /dev/null +++ b/gama.library/models/GAML Syntax/System/Buffering execution order.gaml @@ -0,0 +1,43 @@ +/** +* Name: Bufferingexecutionorder +* Based on the internal empty template. +* Author: baptiste +* Tags: +*/ + + +// This model presents the order of execution of different results you can obtain by using different buffering strategies. +model Bufferingexecutionorder + +global { + + + reflex at_cycle { + write "at cycle " + cycle buffering:"per_cycle"; + save "at cycle " + cycle to:"data.csv" header:false rewrite:false buffering:"per_cycle" format:"csv"; + } + + reflex at_cycle2 { + write "at cycle2 " + cycle + " should appear after 'at cycle "+ cycle+"' as it's asked in that order" buffering:"per_cycle"; + save "at cycle2 " + cycle + " should appear after 'at cycle "+ cycle+"' as it's asked in that order" to:"data.csv" header:false rewrite:false buffering:"per_cycle" format:"csv"; + } + + reflex no_buffering { + write "at cycle " + cycle + " too, should appear before all the other, as it's executed right when the code is reached" buffering:"no_buffering"; + save "at cycle " + cycle + " too, should appear before all the other, as it's executed right when the code is reached" rewrite:false to:"data.csv" header:false buffering:"no_buffering" format:"csv"; + } + + reflex end_of_simulation { + write "Run at cycle " + cycle + " but should only be visible once the simulation is killed." buffering:"per_simulation"; + save "Run at cycle " + cycle + " but should be appended at the end of the file" to:"data.csv" header:false rewrite:false buffering:"per_simulation" format:"csv"; + } + + reflex end_sim when:cycle=4{ + do die; + } + +} + +experiment a type:batch until:cycle=10 autorun:true{ + +} \ No newline at end of file diff --git a/gama.library/models/GAML Syntax/System/Buffering performances.gaml b/gama.library/models/GAML Syntax/System/Buffering performances.gaml new file mode 100644 index 0000000000..3b6ae1dba4 --- /dev/null +++ b/gama.library/models/GAML Syntax/System/Buffering performances.gaml @@ -0,0 +1,51 @@ +/** +* Name: Buffering +* Based on the internal empty template. +* Author: baptiste +* Tags: +*/ + + +// This model presents the difference in terms of performance between writing many times directly in a file +// or using a buffering strategy to have all the writing requests grouped into one big at the end of the cycle +// this benchmarking is not using the "benchmark" statement as we also want to include the time it takes for the +// engine to actually write the files at the end of the cycle. +model BufferingPerformances + + +global{ + + int state <- 0; + int nb_rep <- 10000; + float start_time; + + reflex write_duration_buffered when:state=3{ + write "Duration for buffered writing: " + (gama.machine_time - start_time) + "ms"; + do pause; + } + + reflex write_buffered when:state = 2 { + start_time <- gama.machine_time; + loop times:nb_rep{ + save "some text\n" to:"someotherfile.txt" rewrite:false buffering:"per_cycle" format:"txt"; + } + state <- 3; + } + + reflex write_duration_direct when:state = 1 { + write "Duration for direct writing: " + (gama.machine_time - start_time) + "ms"; + state <- 2; + } + + + reflex write_directly when:state=0{ + start_time <- gama.machine_time; + loop times:nb_rep{ + save "some text\n" to:"somefile.txt" rewrite:false buffering:"no_buffering" format:"txt"; + } + state <- 1; + } + +} + +experiment compare; \ No newline at end of file