From 1a7cd377412f8cb6ff6e657e28b8abdb75439177 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Sat, 21 Jun 2025 12:25:46 +1000 Subject: [PATCH] attempted to improve ingest perfomance by only saving on section unload --- .../common/world/ActiveSectionTracker.java | 17 +++++++++++ .../cortex/voxy/common/world/WorldEngine.java | 11 +++++-- .../voxy/common/world/WorldSection.java | 29 +++++++++++++++++-- .../world/service/SectionSavingService.java | 7 +++-- 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/main/java/me/cortex/voxy/common/world/ActiveSectionTracker.java b/src/main/java/me/cortex/voxy/common/world/ActiveSectionTracker.java index c71194bd..95f6a8f2 100644 --- a/src/main/java/me/cortex/voxy/common/world/ActiveSectionTracker.java +++ b/src/main/java/me/cortex/voxy/common/world/ActiveSectionTracker.java @@ -190,6 +190,23 @@ public class ActiveSectionTracker { void tryUnload(WorldSection section) { if (this.engine != null) this.engine.lastActiveTime = System.currentTimeMillis(); + if (section.isDirty&&this.engine!=null) { + if (section.tryAcquire()) { + if (section.setNotDirty()) {//If the section is dirty we must enqueue for saving + this.engine.saveSection(section); + } + section.release(false);//Special + } else { + VarHandle.loadLoadFence(); + if (section.isDirty) { + throw new IllegalStateException("Section was dirty but is also unloaded, this is very bad"); + } + } + } + + if (section.getRefCount() != 0) { + return; + } int index = this.getCacheArrayIndex(section.key); final var cache = this.loadedSectionCache[index]; WorldSection sec = null; diff --git a/src/main/java/me/cortex/voxy/common/world/WorldEngine.java b/src/main/java/me/cortex/voxy/common/world/WorldEngine.java index d83e0ba7..cf3334eb 100644 --- a/src/main/java/me/cortex/voxy/common/world/WorldEngine.java +++ b/src/main/java/me/cortex/voxy/common/world/WorldEngine.java @@ -118,8 +118,8 @@ public class WorldEngine { if (this.dirtyCallback != null) { this.dirtyCallback.accept(section, changeState); } - if (this.saveCallback != null) { - this.saveCallback.save(this, section); + if (!section.inSaveQueue) { + section.markDirty(); } } @@ -181,4 +181,11 @@ public class WorldEngine { //TODO: maybe dont need to tick the last active time? this.lastActiveTime = System.currentTimeMillis(); } + + public void saveSection(WorldSection section) { + section.setNotDirty(); + if (this.saveCallback != null) { + this.saveCallback.save(this, section); + } + } } diff --git a/src/main/java/me/cortex/voxy/common/world/WorldSection.java b/src/main/java/me/cortex/voxy/common/world/WorldSection.java index aef9c187..1d33eda9 100644 --- a/src/main/java/me/cortex/voxy/common/world/WorldSection.java +++ b/src/main/java/me/cortex/voxy/common/world/WorldSection.java @@ -22,12 +22,16 @@ public final class WorldSection { static final VarHandle ATOMIC_STATE_HANDLE; private static final VarHandle NON_EMPTY_CHILD_HANDLE; private static final VarHandle NON_EMPTY_BLOCK_HANDLE; + private static final VarHandle IN_SAVE_QUEUE_HANDLE; + private static final VarHandle IS_DIRTY_HANDLE; static { try { ATOMIC_STATE_HANDLE = MethodHandles.lookup().findVarHandle(WorldSection.class, "atomicState", int.class); NON_EMPTY_CHILD_HANDLE = MethodHandles.lookup().findVarHandle(WorldSection.class, "nonEmptyChildren", byte.class); NON_EMPTY_BLOCK_HANDLE = MethodHandles.lookup().findVarHandle(WorldSection.class, "nonEmptyBlockCount", int.class); + IN_SAVE_QUEUE_HANDLE = MethodHandles.lookup().findVarHandle(WorldSection.class, "inSaveQueue", boolean.class); + IS_DIRTY_HANDLE = MethodHandles.lookup().findVarHandle(WorldSection.class, "isDirty", boolean.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } @@ -55,7 +59,8 @@ public final class WorldSection { volatile byte nonEmptyChildren; final ActiveSectionTracker tracker; - public final AtomicBoolean inSaveQueue = new AtomicBoolean(); + volatile boolean inSaveQueue; + volatile boolean isDirty; //When the first bit is set it means its loaded @SuppressWarnings("all") @@ -131,6 +136,10 @@ public final class WorldSection { //TODO: add the ability to hint to the tracker that yes the section is unloaded, try to cache it in a secondary cache since it will be reused/needed later public int release() { + return release(true); + } + + int release(boolean unload) { int state = ((int) ATOMIC_STATE_HANDLE.getAndAdd(this, -2)) - 2; if (state < 1) { throw new IllegalStateException("Section got into an invalid state"); @@ -138,7 +147,7 @@ public final class WorldSection { if ((state & 1) == 0) { throw new IllegalStateException("Tried releasing a freed section"); } - if ((state>>1)==0) { + if ((state>>1)==0 && unload) { if (this.tracker != null) { this.tracker.tryUnload(this); } else { @@ -271,4 +280,20 @@ public final class WorldSection { public static WorldSection _createRawUntrackedUnsafeSection(int lvl, int x, int y, int z) { return new WorldSection(lvl, x, y, z, null); } + + public boolean exchangeIsInSaveQueue(boolean state) { + return ((boolean) IN_SAVE_QUEUE_HANDLE.compareAndExchange(this, !state, state)) == !state; + } + + public void markDirty() { + IS_DIRTY_HANDLE.getAndSet(this, true); + } + + public boolean setNotDirty() { + return (boolean) IS_DIRTY_HANDLE.getAndSet(this, false); + } + + public boolean isFreed() { + return (((int)ATOMIC_STATE_HANDLE.get(this))&1)==0; + } } \ No newline at end of file diff --git a/src/main/java/me/cortex/voxy/common/world/service/SectionSavingService.java b/src/main/java/me/cortex/voxy/common/world/service/SectionSavingService.java index 809fece3..86479664 100644 --- a/src/main/java/me/cortex/voxy/common/world/service/SectionSavingService.java +++ b/src/main/java/me/cortex/voxy/common/world/service/SectionSavingService.java @@ -28,8 +28,9 @@ public class SectionSavingService { var section = task.section; section.assertNotFree(); try { - section.inSaveQueue.set(false); - task.engine.storage.saveSection(section); + if (section.exchangeIsInSaveQueue(false)) { + task.engine.storage.saveSection(section); + } } catch (Exception e) { Logger.error("Voxy saver had an exception while executing please check logs and report error", e); } @@ -47,7 +48,7 @@ public class SectionSavingService { public void enqueueSave(WorldEngine in, WorldSection section) { //If its not enqueued for saving then enqueue it - if (!section.inSaveQueue.getAndSet(true)) { + if (section.exchangeIsInSaveQueue(true)) { //Acquire the section for use section.acquire();