feat: optimize RegionizedTaskQueue TTL and reduce ticket operations in nether portal searching

This commit is contained in:
Helvetica Volubi
2026-08-01 16:38:23 +08:00
parent d806e4a423
commit 302825cc7e
4 changed files with 506 additions and 1 deletions
@@ -0,0 +1,146 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sat, 1 Aug 2026 16:30:53 +0800
Subject: [PATCH] Anonymous Object: RegionizedTaskQueue queue TTL optimization
As requested by the original author, this patch will be anonymous and hide the original commit address.
This patch is used with permission from the original author.
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/io/papermc/paper/threadedregions/RegionizedTaskQueue.java b/io/papermc/paper/threadedregions/RegionizedTaskQueue.java
index fe0a6bd90cba4869a0ee11723832f2dee5039f62..53579b6faa9d55485088d59ec2fd1e4251c2fb01 100644
--- a/io/papermc/paper/threadedregions/RegionizedTaskQueue.java
+++ b/io/papermc/paper/threadedregions/RegionizedTaskQueue.java
@@ -23,6 +23,7 @@ import java.util.concurrent.atomic.AtomicLong;
public final class RegionizedTaskQueue {
private static final TicketType<Long> TASK_QUEUE_TICKET = ChunkSystemTicketType.create("task_queue_ticket", Long::compareTo);
+ private static final long QUEUE_MAX_TTL_TICKS = 5L; // Anonymous - RegionizedTaskQueue queue TTL optimization
public PrioritisedExecutor.PrioritisedTask createChunkTask(final ServerLevel world, final int chunkX, final int chunkZ,
final Runnable run) {
@@ -155,6 +156,58 @@ public final class RegionizedTaskQueue {
}
}
+ // Anonymous start - RegionizedTaskQueue queue TTL optimization
+ public void tickQueueReferenceTTL() {
+ final ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> currentRegion
+ = io.papermc.paper.threadedregions.TickRegionScheduler.getCurrentRegion();
+ if (currentRegion == null) {
+ return;
+ }
+
+ final ReferenceCountData[] toRemoveTicket = new ReferenceCountData[1];
+
+ for (ConcurrentChainedLong2ReferenceHashTable.TableEntry<ReferenceCountData> counterEntry : this.referenceCounters.entrySet()) {
+ final long coord = counterEntry.getKey();
+ final ReferenceCountData counterData = counterEntry.getValue();
+
+ // only tick for our region
+ if (currentRegion == this.world.regioniser.getRegionAtUnsynchronised(CoordinateUtils.getChunkX(coord), CoordinateUtils.getChunkZ(coord))) {
+ long curr = counterData.referenceTTL.get();
+ // successfully decreased ttl
+ if (curr == (curr = counterData.referenceTTL.compareAndExchange(curr, curr - 1))) {
+ if (counterData.referenceCount.get() != 0L) {
+ // still has reference, pump back
+ counterData.referenceTTL.set(QUEUE_MAX_TTL_TICKS);
+ continue;
+ }
+
+ // dead
+ if (curr <= 0) {
+ // parsed from decrementReference
+ this.referenceCounters.computeIfPresent(coord, (final long keyInMap, final ReferenceCountData valueInMap) -> {
+ // might be increased again
+ if (valueInMap.referenceCount.get() != 0L) {
+ valueInMap.referenceTTL.set(QUEUE_MAX_TTL_TICKS); // still has reference, pump back
+ return valueInMap; // directly, the ttl was already charged in add logic
+ }
+
+ // note: valueInMap may not be referenceCountData
+ toRemoveTicket[0] = valueInMap;
+
+ return null;
+ });
+
+ if (toRemoveTicket[0] != null) {
+ this.removeTicket(coord, toRemoveTicket[0].id);
+ toRemoveTicket[0] = null;
+ }
+ }
+ }
+ }
+ }
+ }
+ // Anonymous end - RegionizedTaskQueue queue TTL optimization
+
private void decrementReference(final ReferenceCountData referenceCountData, final long coord) {
if (!referenceCountData.decreaseReferenceCount()) {
return;
@@ -212,9 +265,10 @@ public final class RegionizedTaskQueue {
private final long id = ID_GENERATOR.getAndIncrement();
public final AtomicLong referenceCount = new AtomicLong(1L);
+ public final AtomicLong referenceTTL = new AtomicLong(QUEUE_MAX_TTL_TICKS); // Anonymous - RegionizedTaskQueue queue TTL optimization
public volatile boolean addedTicket;
- // returns false if reference count is 0, otherwise increments ref count
+ // returns false if reference count or ttl is 0, otherwise increments ref count // Anonymous - RegionizedTaskQueue queue TTL optimization
public boolean addCount() {
int failures = 0;
for (long curr = this.referenceCount.get();;) {
@@ -227,6 +281,27 @@ public final class RegionizedTaskQueue {
}
if (curr == (curr = this.referenceCount.compareAndExchange(curr, curr + 1L))) {
+ // Anonymous start - RegionizedTaskQueue queue TTL optimization
+ // now force charge back the ttl to max
+ int ttlFailures = 0;
+ for (long currTTL = this.referenceTTL.get();;) {
+ for (int i = 0; i < ttlFailures; i++) {
+ Thread.onSpinWait();
+ }
+
+ // add failed, rollback (ttl reached)
+ if (currTTL <= 0) {
+ this.referenceCount.decrementAndGet(); // revert the addition
+ return false;
+ }
+
+ if (currTTL == (currTTL = this.referenceTTL.compareAndExchange(currTTL, QUEUE_MAX_TTL_TICKS))) {
+ break;
+ }
+
+ ++ttlFailures;
+ }
+ // Anonymous end - RegionizedTaskQueue queue TTL optimization
return true;
}
@@ -234,11 +309,11 @@ public final class RegionizedTaskQueue {
}
}
- // returns true if new reference count is 0
+ // returns true if new reference count and ttl is 0 // Anonymous - RegionizedTaskQueue queue TTL optimization
public boolean decreaseReferenceCount() {
final long res = this.referenceCount.decrementAndGet();
if (res >= 0L) {
- return res == 0L;
+ return res == 0L && this.referenceTTL.get() <= 0L; // Anonymous - RegionizedTaskQueue queue TTL optimization
} else {
throw new IllegalStateException("Negative reference count");
}
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 6c5cdfebb3a362c51b3b940397e10536dc67afc8..4a00b2399a190b609f7b0944e407f3b087e617b5 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1756,6 +1756,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
foliaProfiler.stopTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.PASSENGER_DESYNC_CHECK);
}
// Folia end - fix passenger desync
+ region.world.taskQueueRegionData.tickQueueReferenceTTL(); // Anonymous - RegionizedTaskQueue queue TTL optimization
}
// Folia end - region threading
//this.tickCount++; // Folia - region threading
@@ -0,0 +1,47 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sat, 1 Aug 2026 16:31:57 +0800
Subject: [PATCH] Anonymous Object: Reduce ticket operations in nether portal
searching
As requested by the original author, this patch will be anonymous and hide the original commit address.
This patch is used with permission from the original author.
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 132122f23a697109696852cf383fa1b0186e0210..619521d8ff74c92a822bde9c160931cdfdb7da43 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -221,6 +221,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
private final StructureCheck structureCheck;
public final boolean tickTime; // Folia - region threading
private final LevelDebugSynchronizers debugSynchronizers = new LevelDebugSynchronizers(this);
+ public final io.anonymous.anonymous.utils.ReferenceCountingChunkLoader portalSearchingChunkLoader = new io.anonymous.anonymous.utils.ReferenceCountingChunkLoader(this, net.minecraft.world.level.chunk.status.ChunkStatus.EMPTY); // Anonymous - Reduce ticket operations in nether portal searching
// Luminol start - Level schedulers
private static final java.util.concurrent.atomic.AtomicInteger UNLOADER_ID_GEN = new java.util.concurrent.atomic.AtomicInteger(0);
public final me.earthme.luminol.utils.thread.LevelAwareRegionScheduler levelScheduler = new me.earthme.luminol.utils.thread.LevelAwareRegionScheduler(io.papermc.paper.threadedregions.TickRegions.getScheduler().scheduler);
@@ -1004,6 +1005,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGet
this.debugSynchronizers.tick(this.server.debugSubscribers());
profiler.pop();
+ this.portalSearchingChunkLoader.tickChunkReferenceTTL(); // Anonymous - Reduce ticket operations in nether portal searching
}
// Folia start - region threading
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 489011f9af49fa3407038bd2f00439443307d179..340e6ad83ba93f3927a08a107e3c8dbafa546112 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -4870,10 +4870,10 @@ public abstract class Entity
);
// kick off search for existing portal or creation
- destination.moonrise$loadChunksAsync(
+ destination.portalSearchingChunkLoader.loadChunksAsync( // Anonymous - Reduce ticket operations in nether portal searching
// add 32 so that the final search for a portal frame doesn't load any chunks
targetPos, portalSearchRadius + 32,
- net.minecraft.world.level.chunk.status.ChunkStatus.EMPTY,
+ //net.minecraft.world.level.chunk.status.ChunkStatus.EMPTY, // Anonymous - Reduce ticket operations in nether portal searching
ca.spottedleaf.concurrentutil.util.Priority.HIGH,
(chunks) -> {
BlockUtil.FoundRectangle portal =
@@ -37,7 +37,7 @@ import java.util.logging.Level;
@SuppressWarnings("unused")
public class KaiijuEntityLimits {
private static final Logger LOGGER = LogUtils.getLogger();
private static final File CONFIG_FOLDER = new File("shiroha_config");
private static final File CONFIG_FOLDER = new File("luminol_config");
protected static final String HEADER =
"Per region entity limits for Kaiiju.\n"
@@ -0,0 +1,312 @@
package io.anonymous.anonymous.utils;
import ca.spottedleaf.concurrentutil.completable.CallbackCompletable;
import ca.spottedleaf.concurrentutil.map.concurrent.longs.ConcurrentChainedLong2ReferenceHashTable;
import ca.spottedleaf.concurrentutil.util.Priority;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
import io.papermc.paper.threadedregions.RegionizedServer;
import io.papermc.paper.threadedregions.ThreadedRegionizer;
import io.papermc.paper.threadedregions.TickRegions;
import it.unimi.dsi.fastutil.Pair;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import org.jspecify.annotations.NonNull;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
/**
* 一个简单的基于folia的RegionizedTaskQueue引用计数的轻量ticket区块加载器
* 用于在folia传送门搜索的高频率scheduleChunkLoad的调用下减少ticket操作从而减轻锁负载
* 大部分内容物均取自folia的RegionizedTaskQueue(引用计数), ttl机制取自我的优化
*
* @see io.papermc.paper.threadedregions.RegionizedTaskQueue
* @see Entity#findOrCreatePortalAsync(ServerLevel, BlockPos, ServerLevel, Entity.PortalType, CallbackCompletable)
*/
public class ReferenceCountingChunkLoader {
private static final long MAX_CHUNK_TTL_TICKS = 2L;
private final ServerLevel world;
private final ChunkStatus targetStatus;
private final ConcurrentChainedLong2ReferenceHashTable<ReferenceCountData> referenceCounters = new ConcurrentChainedLong2ReferenceHashTable<>();
public ReferenceCountingChunkLoader(ServerLevel world, ChunkStatus targetStatus) {
this.world = world;
this.targetStatus = targetStatus;
}
private void decrementReference(final @NonNull ReferenceCountData referenceCountData, final long coord) {
if (!referenceCountData.decreaseReferenceCount()) {
return;
}
final ReferenceCountData[] toRemoveTicket = new ReferenceCountData[1];
this.referenceCounters.computeIfPresent(coord, (final long keyInMap, final ReferenceCountData valueInMap) -> {
if (valueInMap.referenceCount.get() != 0L) {
return valueInMap;
}
toRemoveTicket[0] = valueInMap;
return null;
});
if (toRemoveTicket[0] != null) {
this.removeTicket(coord, toRemoveTicket[0].id);
}
}
private void removeTicket(final long coord, final long id) {
this.world.moonrise$getChunkTaskScheduler().chunkHolderManager.removeTicketAtLevel(
ChunkTaskScheduler.CHUNK_LOAD, coord, ChunkTaskScheduler.getTicketLevel(this.targetStatus), Long.valueOf(id)
);
}
private void addTicket(final long coord, final long id) {
this.world.moonrise$getChunkTaskScheduler().chunkHolderManager.addTicketAtLevel(
ChunkTaskScheduler.CHUNK_LOAD, coord, ChunkTaskScheduler.getTicketLevel(this.targetStatus), Long.valueOf(id)
);
}
private void processTicketUpdates(final long coord) {
this.world.moonrise$getChunkTaskScheduler().chunkHolderManager.processTicketUpdates(CoordinateUtils.getChunkX(coord), CoordinateUtils.getChunkZ(coord));
}
private void ensureTicketAdded(final long coord, final @NonNull ReferenceCountData referenceCountData) {
if (!referenceCountData.addedTicket) {
this.addTicket(coord, referenceCountData.id);
this.processTicketUpdates(coord);
referenceCountData.addedTicket = true;
}
}
public final void loadChunksAsync(final @NonNull BlockPos pos, final int radiusBlocks,
final Priority priority,
final Consumer<List<ChunkAccess>> onLoad) {
this.loadChunksAsync(
(pos.getX() - radiusBlocks) >> 4,
(pos.getX() + radiusBlocks) >> 4,
(pos.getZ() - radiusBlocks) >> 4,
(pos.getZ() + radiusBlocks) >> 4,
priority, onLoad
);
}
public final void loadChunksAsync(final int minChunkX, final int maxChunkX, final int minChunkZ, final int maxChunkZ,
final Priority priority,
final Consumer<List<ChunkAccess>> onLoad) {
this.loadChunksAsync(minChunkX, maxChunkX, minChunkZ, maxChunkZ, priority, onLoad, null);
}
public final void loadChunksAsync(final int minChunkX, final int maxChunkX, final int minChunkZ, final int maxChunkZ,
final Priority priority,
final Consumer<List<ChunkAccess>> onLoad, final Consumer<ChunkAccess> onEachLoad) {
final int requiredChunks = (maxChunkX - minChunkX + 1) * (maxChunkZ - minChunkZ + 1);
final AtomicInteger loadedChunks = new AtomicInteger();
final List<Pair<ReferenceCountData, ChunkAccess>> ret = new ArrayList<>(requiredChunks);
final Consumer<ChunkAccess> consumer = (final ChunkAccess chunk) -> {
if (chunk != null) {
final long pos = chunk.getPos().longKey();
synchronized (ret) {
ret.add(Pair.of(this.incrementReference(pos), chunk));
}
}
if (onEachLoad != null) {
onEachLoad.accept(chunk);
}
if (loadedChunks.incrementAndGet() == requiredChunks) {
try {
if (onLoad != null) {
final List<ChunkAccess> processed = new ArrayList<>(ret.size());
for (Pair<ReferenceCountData, ChunkAccess> result : ret) {
processed.add(result.right());
}
onLoad.accept(processed);
}
} finally {
for (Pair<ReferenceCountData, ChunkAccess> extraRefEntry : ret) {
this.decrementReference(extraRefEntry.left(), extraRefEntry.right().getPos().longKey());
}
}
}
};
for (int cx = minChunkX; cx <= maxChunkX; ++cx) {
for (int cz = minChunkZ; cz <= maxChunkZ; ++cz) {
this.loadAsync(cx, cz, consumer, priority);
}
}
}
public void loadAsync(int chunkX, int chunkZ, Consumer<ChunkAccess> callback, Priority priority) {
final long coord = CoordinateUtils.getChunkKey(chunkX, chunkZ);
final ReferenceCountData increased = this.incrementReference(coord);
final ChunkAccess cached = increased.cached;
if (cached != null) {
RegionizedServer.getInstance().taskQueue.queueChunkTask(this.world, chunkX, chunkZ, () -> {
try {
callback.accept(cached);
}finally {
this.decrementReference(increased, coord);
}
}, priority);
return;
}
this.world.moonrise$getChunkTaskScheduler().scheduleChunkLoad(
chunkX, chunkZ, this.targetStatus, true, priority,
chunk -> {
try {
increased.cached = chunk;
callback.accept(chunk);
}finally {
this.decrementReference(increased, coord);
}
}
);
}
private ReferenceCountData incrementReference(final long coord) {
ReferenceCountData referenceCountData = this.referenceCounters.get(coord);
if (referenceCountData != null && referenceCountData.addCount()) {
this.ensureTicketAdded(coord, referenceCountData);
return referenceCountData;
}
referenceCountData = this.referenceCounters.compute(coord, (final long keyInMap, final ReferenceCountData valueInMap) -> {
if (valueInMap == null) {
return new ReferenceCountData();
}
valueInMap.referenceCount.getAndIncrement();
return valueInMap;
});
this.ensureTicketAdded(coord, referenceCountData);
return referenceCountData;
}
public void tickChunkReferenceTTL() {
final ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> currentRegion
= io.papermc.paper.threadedregions.TickRegionScheduler.getCurrentRegion();
if (currentRegion == null) {
return;
}
final ReferenceCountData[] toRemoveTicket = new ReferenceCountData[1];
for (ConcurrentChainedLong2ReferenceHashTable.TableEntry<ReferenceCountData> counterEntry : this.referenceCounters.entrySet()) {
final long coord = counterEntry.getKey();
final ReferenceCountData counterData = counterEntry.getValue();
if (currentRegion == this.world.regioniser.getRegionAtUnsynchronised(CoordinateUtils.getChunkX(coord), CoordinateUtils.getChunkZ(coord))) {
long curr = counterData.referenceTTL.get();
if (curr == (curr = counterData.referenceTTL.compareAndExchange(curr, curr - 1))) {
if (counterData.referenceCount.get() != 0L) {
counterData.referenceTTL.set(MAX_CHUNK_TTL_TICKS);
continue;
}
if (curr <= 0) {
this.referenceCounters.computeIfPresent(coord, (final long keyInMap, final ReferenceCountData valueInMap) -> {
if (valueInMap.referenceCount.get() != 0L) {
valueInMap.referenceTTL.set(MAX_CHUNK_TTL_TICKS);
return valueInMap;
}
toRemoveTicket[0] = valueInMap;
return null;
});
if (toRemoveTicket[0] != null) {
this.removeTicket(coord, toRemoveTicket[0].id);
toRemoveTicket[0] = null;
}
}
}
}
}
}
private static final class ReferenceCountData {
private static final AtomicLong ID_GENERATOR = new AtomicLong();
private final long id = ID_GENERATOR.getAndIncrement();
public final AtomicLong referenceCount = new AtomicLong(1L);
public final AtomicLong referenceTTL = new AtomicLong(MAX_CHUNK_TTL_TICKS);
public volatile ChunkAccess cached;
public volatile boolean addedTicket;
public boolean addCount() {
int failures = 0;
for (long curr = this.referenceCount.get();;) {
for (int i = 0; i < failures; ++i) {
Thread.onSpinWait();
}
if (curr == 0L) {
return false;
}
if (curr == (curr = this.referenceCount.compareAndExchange(curr, curr + 1L))) {
int ttlFailures = 0;
for (long currTTL = this.referenceTTL.get();;) {
for (int i = 0; i < ttlFailures; i++) {
Thread.onSpinWait();
}
if (currTTL <= 0) {
this.referenceCount.decrementAndGet();
return false;
}
if (currTTL == (currTTL = this.referenceTTL.compareAndExchange(currTTL, MAX_CHUNK_TTL_TICKS))) {
break;
}
++ttlFailures;
}
return true;
}
++failures;
}
}
public boolean decreaseReferenceCount() {
final long res = this.referenceCount.decrementAndGet();
if (res >= 0L) {
return res == 0L && this.referenceTTL.get() <= 0L;
} else {
throw new IllegalStateException("Negative reference count");
}
}
}
}