Conversation
paper-server/src/main/java/io/papermc/paper/util/concurrent/ScheduledTask.java
Outdated
Show resolved
Hide resolved
paper-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java
Outdated
Show resolved
Hide resolved
paper-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
Outdated
Show resolved
Hide resolved
paper-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
Outdated
Show resolved
Hide resolved
paper-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
Outdated
Show resolved
Hide resolved
paper-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java
Outdated
Show resolved
Hide resolved
paper-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java
Outdated
Show resolved
Hide resolved
|
This looks like this breaks same tick task execution, and does so in a manner that would create even weirder unexpected behavior to plugins; as well as the order promised by the impl of FIFO which was pointed out to me |
paper-server/src/main/java/io/papermc/paper/util/concurrent/TimingWheel.java
Show resolved
Hide resolved
|
Timing wheel with sorted linked list based on task creation, there is a small additional overhead for the |
|
I feel like after latest changes (especially the part of inserting the tasks in a sorted manner) makes the worst case of this optimisation way worse than the single sorted queue, with a huge weakly related changes to such core components that it makes it dangerous on review. Maybe consider making the wheel based on sorted queues and minimising the diff in CraftScheduler, while also making the exponent configurable (exponent of 0 would mean a single sorted queue so basically old behaviour) |
Worst case you mean a ton of tasks scheduled in the same tick?
I mean sure, I did minize the diff in the CraftScheduler, most diff is just tabbing because there is a new while cycle and cycling the task obtained through the timing wheel, but I didn't change anything else, beside exposing some methods
I can do it, but what would the benefit be of adding a exponent 0 case? If it is only used in that section of the code, it would be better to just throw (with proper documentation ofc) |
A lot of tasks scheduled on same tick, then adding a hundred tasks that were scheduled on every 1/2/5 ticks (so they're most probably the oldest)
In combination with redoing to be based on priority queues, the benefit is complete old behaviour |
After some trial and errors, priority queues are not the way to go for any kind of optimization, using them together with the TimingWheel gives worse results than just using a single priority queue Instead of sorting at add (and making the worst case scenario worse), I am now sorting on pop using getCommand("task_same_bucket").setExecutor(
(sender, command, label, args) -> {
for (int i = 0; i < 100_000; i++) {
schdl.runTaskTimer(
TaskSpawner.this,
new Runnable() {
long x = new Random().nextLong();
@Override
public void run() {
if (x % 2 == 0) {
x /= 2;
} else {
x *= 3;
x++;
}
}
}, 1L, 1L
);
}
Bukkit.broadcast(Component.text("Completed"));
return true;
} |
|
I can make a static method that given an int returns a scheduler using priorityQueue when 0 or less is supplied and a timing wheel otherwise if you still want |
An optimization for the task scheduler, instead of using PriorityQueue (which has O(log n) operations), use a Timing Wheel (which has O(1) within one round execution).
I tested this in one of the plugins I work with and had good improvements from it.
I tested and spawned 100_000 tasks using a plugin, the server has 8GB max ram and
/spark profiler start --timeout 60 --thread *1.21.11 paper build 127 [PriorityQueue]
https://spark.lucko.me/ghhGHA8Pq7
CraftScheduler#mainThreadHeartbeat()took 23.49ms of which18.14ms for removal and 1.33ms for insertion
1.21.11 paper opt/timing-wheel branch [TimingWheel]
https://spark.lucko.me/HUNomOcLXC
CraftScheduler#mainThreadHeartbeat()took 9.49ms of which2.64ms for removal and 2.12ms for insertion
Plugin's testing code: