attempted to improve ingest perfomance by only saving on section unload

This commit is contained in:
mcrcortex
2025-06-21 12:25:46 +10:00
parent ed181c1dcd
commit 1a7cd37741
4 changed files with 57 additions and 7 deletions

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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();