mirror of
https://github.com/SparklyPower/SparklyPaper.git
synced 2025-12-29 11:59:06 +00:00
Skip EntityScheduler's executeTick checks if there isn't any tasks to be run
On each tick, Paper runs EntityScheduler's executeTick of each entity. This is a bit expensive, due to ArrayDeque's size() call because it ain't a simple "get the current queue size" function, and due to the thread checks. To avoid the hefty ArrayDeque's size() call, we check if we *really* need to execute the executeTick, by checking if currentlyExecuting is not empty or if oneTimeDelayed is not empty. Most entities won't have any scheduled tasks, so this is a nice performance bonus. These optimizations, however, wouldn't work in a Folia environment, but because in SparklyPaper executeTick is always executed on the main thread, it ain't an issue for us (yay).
This commit is contained in:
@@ -34,6 +34,11 @@ SparklyPaper's config file is `sparklypaper.yml`, the file is, by default, place
|
||||
* When ticking an item frame, on each tick, it checks if the item on the item frame is a map and, if it is, it adds the map to be carried by the entity player
|
||||
* However, the `getItem()` call is a bit expensive, especially because this is only really used if the item in the item frame is a map
|
||||
* We can avoid this call by checking if the `cachedMapId` is not null, if it is, then we get the item in the item frame, if not, then we ignore the `getItem()` call.
|
||||
* Optimize `EntityScheduler`'s `executeTick`
|
||||
* On each tick, Paper runs `EntityScheduler`'s `executeTick` of each entity. This is a bit expensive, due to `ArrayDeque`'s `size()` call because it ain't a simple "get the current queue size" function, and due to the thread checks.
|
||||
* To avoid the hefty `ArrayDeque`'s `size()` call, we check if we *really* need to execute the `executeTick`, by checking if `currentlyExecuting` is not empty or if `oneTimeDelayed` is not empty. This brings `executeTick`'s CPU % from 2.46% down to 1.14% in a server with ~13k entities.
|
||||
* Most entities won't have any scheduled tasks, so this is a nice performance bonus. These optimizations, however, wouldn't work in a Folia environment, but because in SparklyPaper `executeTick` is always executed on the main thread, it ain't an issue for us (yay).
|
||||
* Of course, this doesn't mean that `ArrayDeque#size()` is slow! It is mostly that because the `executeTick` function is called each tick for each entity, it would be better for us to avoid as many useless calls as possible.
|
||||
* Check how much MSPT (milliseconds per tick) each world is using in `/mspt`
|
||||
* Useful to figure out which worlds are lagging your server.
|
||||

|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: MrPowerGamerBR <git@mrpowergamerbr.com>
|
||||
Date: Sun, 19 Nov 2023 12:35:16 -0300
|
||||
Subject: [PATCH] Skip EntityScheduler's executeTick checks if there isn't any
|
||||
tasks to be run
|
||||
|
||||
On each tick, Paper runs EntityScheduler's executeTick of each entity. This is a bit expensive, due to ArrayDeque's size() call because it ain't a simple "get the current queue size" function, and due to the thread checks.
|
||||
|
||||
To avoid the hefty ArrayDeque's size() call, we check if we *really* need to execute the executeTick, by checking if currentlyExecuting is not empty or if oneTimeDelayed is not empty.
|
||||
|
||||
Most entities won't have any scheduled tasks, so this is a nice performance bonus. These optimizations, however, wouldn't work in a Folia environment, but because in SparklyPaper executeTick is always executed on the main thread, it ain't an issue for us (yay).
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java b/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java
|
||||
index 62484ebf4550b05182f693a3180bbac5d5fd906d..1ee31db28d3a4b9b841efeb37f7df7932dfad2dc 100644
|
||||
--- a/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java
|
||||
+++ b/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java
|
||||
@@ -138,15 +138,27 @@ public final class EntityScheduler {
|
||||
* @throws IllegalStateException If the scheduler is retired.
|
||||
*/
|
||||
public void executeTick() {
|
||||
+ // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run
|
||||
+ // This wouldn't work on a multithreaded environment like Folia!!! But because the executeTick is always executed on the
|
||||
+ // main thread, we don't need to care about concurrency
|
||||
+ if (this.tickCount == RETIRED_TICK_COUNT) {
|
||||
+ throw new IllegalStateException("Ticking retired scheduler");
|
||||
+ }
|
||||
+ ++this.tickCount;
|
||||
+ if (this.currentlyExecuting.isEmpty() && this.oneTimeDelayed.isEmpty()) // Check if we have any pending tasks and, if not, skip!
|
||||
+ return;
|
||||
+ // SparklyPaper end
|
||||
final Entity thisEntity = this.entity.getHandleRaw();
|
||||
|
||||
TickThread.ensureTickThread(thisEntity, "May not tick entity scheduler asynchronously");
|
||||
final List<ScheduledTask> toRun;
|
||||
synchronized (this.stateLock) {
|
||||
- if (this.tickCount == RETIRED_TICK_COUNT) {
|
||||
- throw new IllegalStateException("Ticking retired scheduler");
|
||||
- }
|
||||
- ++this.tickCount;
|
||||
+ // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run
|
||||
+ // if (this.tickCount == RETIRED_TICK_COUNT) {
|
||||
+ // throw new IllegalStateException("Ticking retired scheduler");
|
||||
+ // }
|
||||
+ // ++this.tickCount;
|
||||
+ // SparklyPaper end
|
||||
if (this.oneTimeDelayed.isEmpty()) {
|
||||
toRun = null;
|
||||
} else {
|
||||
Reference in New Issue
Block a user