9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-25 09:59:15 +00:00

Reduce AsyncGoal content switching

This commit is contained in:
hayanesuru
2025-05-02 18:33:48 -07:00
parent 1431eff510
commit 1ffa315c13
4 changed files with 42 additions and 55 deletions

View File

@@ -1,5 +1,6 @@
package org.dreeam.leaf.async;
import net.minecraft.Util;
import org.dreeam.leaf.config.modules.async.AsyncPlayerDataSave;
import java.util.Optional;
@@ -17,7 +18,7 @@ public class AsyncPlayerDataSaving {
new com.google.common.util.concurrent.ThreadFactoryBuilder()
.setPriority(Thread.NORM_PRIORITY - 2)
.setNameFormat("Leaf IO Thread")
.setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER))
.setUncaughtExceptionHandler(Util::onThreadException)
.build(),
new ThreadPoolExecutor.DiscardPolicy()
);

View File

@@ -17,20 +17,19 @@ public class AsyncGoalExecutor {
protected final SpscIntQueue queue;
protected final SpscIntQueue wake;
private final AsyncGoalThread thread;
private final ServerLevel serverLevel;
private final ServerLevel world;
private boolean dirty = false;
private long tickCount = 0L;
private static final int SPIN_LIMIT = 100;
public AsyncGoalExecutor(AsyncGoalThread thread, ServerLevel serverLevel) {
this.serverLevel = serverLevel;
public AsyncGoalExecutor(AsyncGoalThread thread, ServerLevel world) {
this.world = world;
this.queue = new SpscIntQueue(AsyncTargetFinding.queueSize);
this.wake = new SpscIntQueue(AsyncTargetFinding.queueSize);
this.thread = thread;
}
boolean wake(int id) {
Entity entity = this.serverLevel.getEntities().get(id);
Entity entity = this.world.getEntities().get(id);
if (entity == null || entity.isRemoved() || !(entity instanceof Mob mob)) {
return false;
}
@@ -40,19 +39,13 @@ public class AsyncGoalExecutor {
}
public final void submit(int entityId) {
if (!this.queue.send(entityId)) {
int spinCount = 0;
while (!this.queue.send(entityId)) {
spinCount++;
// Unpark the thread after some spinning to help clear the queue
if (spinCount > SPIN_LIMIT) {
unpark();
spinCount = 0;
}
Thread.onSpinWait();
}
}
dirty = true;
if (!this.queue.send(entityId)) {
unpark();
do {
wake(entityId);
} while (poll(entityId));
}
}
public final void unpark() {
@@ -61,27 +54,31 @@ public class AsyncGoalExecutor {
}
public final void midTick() {
boolean didWork = false;
while (true) {
int id = this.wake.recv();
if (id == Integer.MAX_VALUE) {
Integer id = this.wake.recv();
if (id == null) {
break;
}
didWork = true;
Entity entity = this.serverLevel.getEntities().get(id);
if (entity == null || !entity.isAlive() || !(entity instanceof Mob mob)) {
continue;
}
mob.tickingTarget = true;
boolean a = mob.targetSelector.poll();
mob.tickingTarget = false;
boolean b = mob.goalSelector.poll();
if (a || b) {
if (poll(id)) {
submit(id);
}
}
if (didWork || (tickCount & 15L) == 0L) unpark();
if ((tickCount & 7L) == 7L) {
unpark();
}
tickCount += 1;
}
private boolean poll(int id) {
Entity entity = this.world.getEntities().get(id);
if (entity == null || !entity.isAlive() || !(entity instanceof Mob mob)) {
return false;
}
mob.tickingTarget = true;
boolean a = mob.targetSelector.poll();
mob.tickingTarget = false;
boolean b = mob.goalSelector.poll();
return a || b;
}
}

View File

@@ -8,8 +8,6 @@ import java.util.concurrent.locks.LockSupport;
public class AsyncGoalThread extends Thread {
private static final int SPIN_TRIES = 1000;
public AsyncGoalThread(final MinecraftServer server) {
super(() -> run(server), "Leaf Async Goal Thread");
this.setDaemon(false);
@@ -19,38 +17,27 @@ public class AsyncGoalThread extends Thread {
}
private static void run(MinecraftServer server) {
int emptySpins = 0;
while (server.isRunning()) {
boolean didWork = false;
boolean retry = false;
for (ServerLevel level : server.getAllLevels()) {
var exec = level.asyncGoalExecutor;
boolean levelWork = false;
while (true) {
int id = exec.queue.recv();
if (id == Integer.MAX_VALUE) {
Integer id = exec.queue.recv();
if (id == null) {
break;
}
levelWork = true;
retry = true;
if (exec.wake(id)) {
while (!exec.wake.send(id)) {
Thread.onSpinWait();
}
}
}
didWork |= levelWork;
}
// Adaptive parking
if (didWork) {
emptySpins = 0; // Reset counter when work was done
if (retry) {
Thread.yield();
} else {
emptySpins++;
if (emptySpins > SPIN_TRIES) {
LockSupport.park(); // Only park after several empty spins
emptySpins = 0;
} else {
Thread.onSpinWait(); // Yield to other threads but don't park
}
LockSupport.park();
}
}
}

View File

@@ -1,5 +1,7 @@
package org.dreeam.leaf.util.queue;
import org.jetbrains.annotations.Nullable;
/// Lock-free Single Producer Single Consumer Queue
public class SpscIntQueue {
@@ -33,14 +35,14 @@ public class SpscIntQueue {
}
public final int recv() {
public final @Nullable Integer recv() {
final int idx = consumerIdx.getOpaque();
int cachedIdx = producerCachedIdx.getPlain();
if (idx == cachedIdx) {
cachedIdx = producerIdx.getAcquire();
producerCachedIdx.setPlain(cachedIdx);
if (idx == cachedIdx) {
return Integer.MAX_VALUE;
return null;
}
}
int e = data[idx];