Skip to content

Commit

Permalink
Merge pull request #335 from gama-platform/clean-buffering
Browse files Browse the repository at this point in the history
  • Loading branch information
lesquoyb authored Sep 23, 2024
2 parents 6346704 + 9f286fd commit c5cc721
Show file tree
Hide file tree
Showing 14 changed files with 129 additions and 81 deletions.
10 changes: 6 additions & 4 deletions gama.core/src/gama/core/kernel/simulation/SimulationAgent.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -968,6 +969,7 @@ public void executeAction(final IExecutable executable) {

}

@SuppressWarnings("unchecked")
@Override
public void updateWith(final IScope scope, final ISerialisedAgent sa) {

Expand Down
4 changes: 2 additions & 2 deletions gama.core/src/gama/core/metamodel/agent/AbstractAgent.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
41 changes: 6 additions & 35 deletions gama.core/src/gama/core/runtime/GAMA.java
Original file line number Diff line number Diff line change
Expand Up @@ -104,41 +104,6 @@ public class GAMA {
// hqnghi: add several controllers to have multi-thread experiments
private static final List<IExperimentController> 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.
Expand All @@ -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
Expand Down
20 changes: 10 additions & 10 deletions gama.core/src/gama/core/runtime/concurrent/BufferingController.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Map<AbstractAgent, TextBuffer>> map) {
protected static boolean flushAllFilesOfAgent(AbstractAgent owner, Map<String, Map<AbstractAgent, TextBuffer>> map) {
boolean success = true;
for(var entry : map.entrySet()) {
var writeTask = entry.getValue().get(owner);
Expand All @@ -326,7 +326,7 @@ protected static boolean flushAllFilesOfOwner(AbstractAgent owner, Map<String, M
* @param owner the agent in which the write statements have been executed
* @param map the map in which to look up
*/
protected static void flushAllWriteOfOwner(final AbstractAgent owner, final Map<AbstractAgent, List<TextBuffer>> map) {
protected static void flushAllWriteOfAgent(final AbstractAgent owner, final Map<AbstractAgent, List<TextBuffer>> map) {
var tasks = map.get(owner);
if (tasks != null) {
var scope = owner.getScope();
Expand All @@ -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);
}

/**
Expand All @@ -354,23 +354,23 @@ 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);
}

/**
* Flushes all the write statement requests made in a simulation with the 'per_cycle' strategy
* @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);
}

/**
Expand All @@ -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()
Expand All @@ -400,7 +400,7 @@ public synchronized void flushAllBuffers() {
.flatMap(Collection::stream)
.toArray(length -> new AbstractAgent[length]);
for (AbstractAgent agent : agents) {
flushSaveFilesOfOwner(agent);
flushSaveFilesOfAgent(agent);
}
}

Expand Down
4 changes: 2 additions & 2 deletions gama.core/src/gama/gaml/operators/Files.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
2 changes: 1 addition & 1 deletion gama.core/src/gama/gaml/statements/BenchmarkStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
2 changes: 1 addition & 1 deletion gama.core/src/gama/gaml/statements/SaveStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion gama.core/src/gama/gaml/statements/WriteStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
23 changes: 2 additions & 21 deletions gama.core/src/gama/gaml/statements/save/CSVSaver.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand All @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion gama.core/src/gama/gaml/statements/save/TextSaver.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down
Original file line number Diff line number Diff line change
@@ -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{

}
51 changes: 51 additions & 0 deletions gama.library/models/GAML Syntax/System/Buffering performances.gaml
Original file line number Diff line number Diff line change
@@ -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;
4 changes: 2 additions & 2 deletions gama.ui.application/src/gama/ui/application/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit c5cc721

Please sign in to comment.