Still very much a draft but I've settled on two approaches:
Calculating the fibonacci sequence either approach performs about 5x better than the curent scheduler.
Fibonacci sequence is thousands of very short operations so it does very well the more the duration check is skipped. Real world tasks won't improve nearly as much but I think most code should get a 10 to 15% speed increase.
First approach skips loops based on how many times it ran last time (This is faster than second but I think more dangerous and only works if you're running a single task):
private int lastLoopCount;
/**
* Execute a list of Tasks that hold RepeatingCommands.
*
* @return A replacement array that is possibly a shorter copy of <code>tasks</code>
*/
private JsArray<Task> runRepeatingTasks(JsArray<Task> tasks) {
assert tasks != null : "tasks";
int length = tasks.length();
if (length == 0) {
return null;
}
boolean canceledSomeTasks = false;
Duration duration = createDuration();
int loopCount = lastLoopCount;
boolean skipLoop = false;
if (length == 1 && lastLoopCount > 0) {
while (lastLoopCount-- > 0 ) {
if (!tasks.get(0).executeRepeating()) {
tasks.set(0, null);
canceledSomeTasks = true;
}
}
skipLoop = canceledSomeTasks || duration.elapsedMillis() >= TIME_SLICE;
}
if (skipLoop) {
lastLoopCount -= 2;
} else {
while (duration.elapsedMillis() < TIME_SLICE) {
boolean executedSomeTask = false;
for (int i = 0; i < length; i++) {
assert tasks.length() == length : "Working array length changed " + tasks.length() + " != "
+ length;
Task t = tasks.get(i);
if (t == null) {
continue;
}
executedSomeTask = true;
assert t.isRepeating() : "Found a non-repeating Task";
if (!t.executeRepeating()) {
tasks.set(i, null);
canceledSomeTasks = true;
}
loopCount += 1;
}
if (!executedSomeTask) {
//no work left to do, break to avoid busy waiting until TIME_SLICE is reached
break;
}
}
if (length == 1) {
lastLoopCount = loopCount;
}
}
if (canceledSomeTasks) {
lastLoopCount = 0;
JsArray<Task> newTasks = createQueue();
// Remove tombstones
for (int i = 0; i < length; i++) {
if (tasks.get(i) != null) {
newTasks.push(tasks.get(i));
}
}
assert newTasks.length() < length;
return newTasks.length() == 0 ? null : newTasks;
} else {
return tasks;
}
}
Second approach checks how much time is elapsed and how many times the task has been run and then calculates how many times the task can be run before it needs to check the elapsed time again. (This is very slightly slower than the above but can be made to work with multiple tasks and I think will be easier to make safe, although isn't safe at the moment):
/**
* Execute a list of Tasks that hold RepeatingCommands.
*
* @return A replacement array that is possibly a shorter copy of
*/
private JsArray<Task> runRepeatingTasks(JsArray<Task> tasks) {
assert tasks != null : "tasks";
int length = tasks.length();
if (length == 0) {
return null;
}
boolean canceledSomeTasks = false;
Duration duration = createDuration();
int runCount = 0;
int quickRunCount = 0;
int elapsedMillis = duration.elapsedMillis();
while ( elapsedMillis < TIME_SLICE) {
boolean executedSomeTask = false;
for (int i = 0; i < length; i++) {
assert tasks.length() == length : "Working array length changed " + tasks.length() + " != " + length;
Task t = tasks.get(i);
if (t == null) {
continue;
}
executedSomeTask = true;
assert t.isRepeating() : "Found a non-repeating Task";
if (!t.executeRepeating()) {
tasks.set(i, null);
canceledSomeTasks = true;
}
}
if (!executedSomeTask) {
// no work left to do, break to avoid busy waiting until
// TIME_SLICE is reached
break;
}
runCount += 1;
if (quickRunCount-- <= 0) {
elapsedMillis = duration.elapsedMillis();
if (elapsedMillis > 0) {
quickRunCount = (int) (((TIME_SLICE - 1) * runCount) / elapsedMillis);
}
}
}
if (canceledSomeTasks) {