Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean the buffering strategies code and provide example models #335

Merged
merged 2 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ Getting worse: Primitive Obsession
The ratio of primitive types in function arguments increases from 30.51% to 39.13%, threshold = 30.0%


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;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ Getting worse: Complex Method
save increases in cyclomatic complexity from 20 to 21, threshold = 9

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 {

}
Comment on lines -156 to -159

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No longer an issue: Excess Number of Function Arguments
save is no longer above the threshold for number of arguments


/**
* 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