9
0
mirror of https://github.com/VolmitSoftware/Iris.git synced 2025-12-19 15:09:18 +00:00

optimize the chunk updater

This commit is contained in:
Julian Krings
2025-08-21 17:27:07 +02:00
parent 8662f3b47a
commit 747e7b3330
4 changed files with 61 additions and 78 deletions

View File

@@ -176,13 +176,13 @@ public class IrisSettings {
@Data
public static class IrisSettingsUpdater {
public double threadMultiplier = 2;
public int maxConcurrency = 256;
public double chunkLoadSensitivity = 0.7;
public MsRange emptyMsRange = new MsRange(80, 100);
public MsRange defaultMsRange = new MsRange(20, 40);
public double getThreadMultiplier() {
return Math.min(Math.abs(threadMultiplier), 0.1);
public int getMaxConcurrency() {
return Math.max(Math.abs(maxConcurrency), 1);
}
public double getChunkLoadSensitivity() {

View File

@@ -39,14 +39,14 @@ public class ChunkUpdater {
private final AtomicInteger chunksUpdated = new AtomicInteger();
private final AtomicBoolean serverEmpty = new AtomicBoolean(true);
private final AtomicLong lastCpsTime = new AtomicLong(M.ms());
private final int coreLimit = (int) Math.max(Runtime.getRuntime().availableProcessors() * IrisSettings.get().getUpdater().getThreadMultiplier(), 1);
private final Semaphore semaphore = new Semaphore(256);
private final LoadBalancer loadBalancer = new LoadBalancer(semaphore, 256, IrisSettings.get().getUpdater().emptyMsRange);
private final int maxConcurrency = IrisSettings.get().getUpdater().getMaxConcurrency();
private final Semaphore semaphore = new Semaphore(maxConcurrency);
private final LoadBalancer loadBalancer = new LoadBalancer(semaphore, maxConcurrency, IrisSettings.get().getUpdater().emptyMsRange);
private final AtomicLong startTime = new AtomicLong();
private final Dimensions dimensions;
private final PregenTask task;
private final ExecutorService executor = Executors.newFixedThreadPool(coreLimit);
private final ExecutorService chunkExecutor = Executors.newFixedThreadPool(coreLimit);
private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
private final ExecutorService chunkExecutor = Executors.newVirtualThreadPerTaskExecutor();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private final CountDownLatch latch;
private final Engine engine;
@@ -138,10 +138,10 @@ public class ChunkUpdater {
loadBalancer.close();
semaphore.acquire(256);
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
chunkExecutor.shutdown();
chunkExecutor.awaitTermination(5, TimeUnit.SECONDS);
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
scheduler.shutdownNow();
unloadAndSaveAllChunks();
} catch (Exception ignored) {}

View File

@@ -296,67 +296,63 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
try {
Semaphore semaphore = new Semaphore(3);
chunk.raiseFlag(MantleFlag.ETCHED, () -> {
chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> J.s(() -> {
mantle.iterateChunk(c.getX(), c.getZ(), TileWrapper.class, (x, y, z, v) -> {
int betterY = y + getWorld().minHeight();
if (!TileData.setTileState(c.getBlock(x, betterY, z), v.getData()))
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", x, betterY, z, c.getBlock(x, betterY, z).getBlockData().getMaterial().getKey(), v.getData().getMaterial().name());
chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> {
chunk.iterate(TileWrapper.class, (x, y, z, v) -> {
Block block = c.getBlock(x & 15, y + getWorld().minHeight(), z & 15);
if (!TileData.setTileState(block, v.getData()))
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", block.getX(), block.getY(), block.getZ(), block.getType().getKey(), v.getData().getMaterial().getKey());
});
})));
chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> J.s(() -> {
mantle.iterateChunk(c.getX(), c.getZ(), Identifier.class, (x, y, z, v) -> {
}, 0));
chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> {
chunk.iterate(Identifier.class, (x, y, z, v) -> {
Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v);
});
})));
}, 0));
chunk.raiseFlag(MantleFlag.UPDATE, run(semaphore, () -> J.s(() -> {
chunk.raiseFlag(MantleFlag.UPDATE, run(semaphore, () -> {
PrecisionStopwatch p = PrecisionStopwatch.start();
KMap<Long, Integer> updates = new KMap<>();
RNG r = new RNG(Cache.key(c.getX(), c.getZ()));
mantle.iterateChunk(c.getX(), c.getZ(), MatterCavern.class, (x, yf, z, v) -> {
int[][] grid = new int[16][16];
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
grid[x][z] = Integer.MIN_VALUE;
}
}
RNG rng = new RNG(Cache.key(c.getX(), c.getZ()));
chunk.iterate(MatterCavern.class, (x, yf, z, v) -> {
int y = yf + getWorld().minHeight();
if (!B.isFluid(c.getBlock(x & 15, y, z & 15).getBlockData())) {
x &= 15;
z &= 15;
Block block = c.getBlock(x, y, z);
if (!B.isFluid(block.getBlockData())) {
return;
}
boolean u = false;
if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.DOWN).getBlockData())) {
u = true;
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.WEST).getBlockData())) {
u = true;
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.EAST).getBlockData())) {
u = true;
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.SOUTH).getBlockData())) {
u = true;
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.NORTH).getBlockData())) {
u = true;
}
boolean u = B.isAir(block.getRelative(BlockFace.DOWN).getBlockData())
|| B.isAir(block.getRelative(BlockFace.WEST).getBlockData())
|| B.isAir(block.getRelative(BlockFace.EAST).getBlockData())
|| B.isAir(block.getRelative(BlockFace.SOUTH).getBlockData())
|| B.isAir(block.getRelative(BlockFace.NORTH).getBlockData());
if (u) {
updates.compute(Cache.key(x & 15, z & 15), (k, vv) -> {
if (vv != null) {
return Math.max(vv, y);
}
return y;
});
}
if (u) grid[x][z] = Math.max(grid[x][z], y);
});
updates.forEach((k, v) -> update(Cache.keyX(k), v, Cache.keyZ(k), c, r));
mantle.iterateChunk(c.getX(), c.getZ(), MatterUpdate.class, (x, yf, z, v) -> {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
if (grid[x][z] == Integer.MIN_VALUE)
continue;
update(x, grid[x][z], z, c, rng);
}
}
chunk.iterate(MatterUpdate.class, (x, yf, z, v) -> {
int y = yf + getWorld().minHeight();
if (v != null && v.isUpdate()) {
int vx = x & 15;
int vz = z & 15;
update(x, y, z, c, new RNG(Cache.key(c.getX(), c.getZ())));
if (vx > 0 && vx < 15 && vz > 0 && vz < 15) {
updateLighting(x, y, z, c);
}
update(x, y, z, c, rng);
}
});
mantle.deleteChunkSlice(c.getX(), c.getZ(), MatterUpdate.class);
chunk.deleteSlices(MatterUpdate.class);
getMetrics().getUpdates().put(p.getMilliseconds());
}, RNG.r.i(0, 20))));
}, RNG.r.i(1, 20))); //Why is there a random delay here?
});
try {
@@ -367,33 +363,21 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
}
}
private static Runnable run(Semaphore semaphore, Runnable runnable) {
private static Runnable run(Semaphore semaphore, Runnable runnable, int delay) {
return () -> {
if (!semaphore.tryAcquire())
return;
J.s(() -> {
try {
runnable.run();
} finally {
semaphore.release();
}
}, delay);
};
}
@BlockCoordinates
default void updateLighting(int x, int y, int z, Chunk c) {
Block block = c.getBlock(x, y, z);
BlockData data = block.getBlockData();
if (B.isLit(data)) {
try {
block.setType(Material.AIR, false);
block.setBlockData(data, true);
} catch (Exception e) {
Iris.reportError(e);
}
}
}
@BlockCoordinates
@Override

View File

@@ -610,8 +610,7 @@ public class B {
}
public static boolean isUpdatable(BlockData mat) {
return isLit(mat)
|| isStorage(mat)
return isStorage(mat)
|| (mat instanceof PointedDripstone
&& ((PointedDripstone) mat).getThickness().equals(PointedDripstone.Thickness.TIP));
}