mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-12-31 12:56:35 +00:00
get back some speed by loading four mantle regions into cache at once
This commit is contained in:
@@ -61,17 +61,22 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
|
||||
this.engineMantle = engineMantle;
|
||||
this.mantle = mantle;
|
||||
this.radius = radius * 2;
|
||||
int d = this.radius + 1;
|
||||
final int d = this.radius + 1;
|
||||
this.cachedChunks = multicore ? new KMap<>(d * d, 0.75f, Math.max(32, Runtime.getRuntime().availableProcessors() * 4)) : new HashMap<>(d * d);
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
|
||||
int r = radius / 2;
|
||||
for (int i = -r; i <= r; i++) {
|
||||
for (int j = -r; j <= r; j++) {
|
||||
cachedChunks.put(Cache.key(i + x, j + z), mantle.getChunk(i + x, j + z).use());
|
||||
}
|
||||
}
|
||||
final int parallelism = multicore ? Runtime.getRuntime().availableProcessors() / 2 : 4;
|
||||
final var map = multicore ? cachedChunks : new KMap<Long, MantleChunk>(d * d, 1f, parallelism);
|
||||
mantle.getChunks(
|
||||
x - radius,
|
||||
x + radius,
|
||||
z - radius,
|
||||
z + radius,
|
||||
parallelism,
|
||||
(i, j, c) -> map.put(Cache.key(i, j), c.use())
|
||||
);
|
||||
if (!multicore) cachedChunks.putAll(map);
|
||||
}
|
||||
|
||||
private static Set<IrisPosition> getBallooned(Set<IrisPosition> vset, double radius) {
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||
import com.volmit.iris.util.documentation.RegionCoordinates;
|
||||
import com.volmit.iris.util.format.C;
|
||||
import com.volmit.iris.util.format.Form;
|
||||
import com.volmit.iris.util.function.Consumer3;
|
||||
import com.volmit.iris.util.function.Consumer4;
|
||||
import com.volmit.iris.util.io.IO;
|
||||
import com.volmit.iris.util.mantle.io.IOWorker;
|
||||
@@ -50,6 +51,9 @@ import java.io.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* The mantle can store any type of data slice anywhere and manage regions & IO on it's own.
|
||||
@@ -201,6 +205,68 @@ public class Mantle {
|
||||
return get(x >> 5, z >> 5).getOrCreate(x & 31, z & 31);
|
||||
}
|
||||
|
||||
public void getChunks(final int minChunkX,
|
||||
final int maxChunkX,
|
||||
final int minChunkZ,
|
||||
final int maxChunkZ,
|
||||
int parallelism,
|
||||
final Consumer3<Integer, Integer, MantleChunk> consumer
|
||||
) {
|
||||
if (parallelism <= 0) parallelism = 1;
|
||||
final var lock = new Semaphore(parallelism);
|
||||
|
||||
final int minRegionX = minChunkX >> 5;
|
||||
final int maxRegionX = maxChunkX >> 5;
|
||||
final int minRegionZ = minChunkZ >> 5;
|
||||
final int maxRegionZ = maxChunkZ >> 5;
|
||||
|
||||
final int minRelativeX = minChunkX & 31;
|
||||
final int maxRelativeX = maxChunkX & 31;
|
||||
final int minRelativeZ = minChunkZ & 31;
|
||||
final int maxRelativeZ = maxChunkZ & 31;
|
||||
|
||||
final AtomicReference<Throwable> error = new AtomicReference<>();
|
||||
for (int rX = minRegionX; rX <= maxRegionX; rX++) {
|
||||
final int minX = rX == minRegionX ? minRelativeX : 0;
|
||||
final int maxX = rX == maxRegionX ? maxRelativeX : 31;
|
||||
for (int rZ = minRegionZ; rZ <= maxRegionZ; rZ++) {
|
||||
final int minZ = rZ == minRegionZ ? minRelativeZ : 0;
|
||||
final int maxZ = rZ == maxRegionZ ? maxRelativeZ : 31;
|
||||
final int realX = rX << 5;
|
||||
final int realZ = rZ << 5;
|
||||
|
||||
lock.acquireUninterruptibly();
|
||||
final var e = error.get();
|
||||
if (e != null) {
|
||||
if (e instanceof RuntimeException ex) throw ex;
|
||||
else if (e instanceof Error ex) throw ex;
|
||||
else throw new RuntimeException(error.get());
|
||||
}
|
||||
|
||||
getFuture(rX, rZ)
|
||||
.thenAccept(region -> {
|
||||
final MantleChunk zero = region.getOrCreate(0, 0).use();
|
||||
try {
|
||||
for (int xx = minX; xx <= maxX; xx++) {
|
||||
for (int zz = minZ; zz <= maxZ; zz++) {
|
||||
consumer.accept(realX + xx, realZ + zz, region.getOrCreate(xx, zz));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
zero.release();
|
||||
}
|
||||
})
|
||||
.exceptionally(ex -> {
|
||||
error.set(ex);
|
||||
return null;
|
||||
})
|
||||
.thenRun(lock::release);
|
||||
}
|
||||
}
|
||||
|
||||
lock.acquireUninterruptibly(parallelism);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag or unflag a chunk
|
||||
*
|
||||
@@ -554,6 +620,53 @@ public class Mantle {
|
||||
return get(x, z);
|
||||
}
|
||||
|
||||
private CompletableFuture<TectonicPlate> getFuture(int x, int z) {
|
||||
final boolean trim = ioTrim.tryAcquire();
|
||||
final boolean unload = ioTectonicUnload.tryAcquire();
|
||||
final Function<TectonicPlate, TectonicPlate> release = p -> {
|
||||
if (trim) ioTrim.release();
|
||||
if (unload) ioTectonicUnload.release();
|
||||
return p;
|
||||
};
|
||||
|
||||
final Supplier<CompletableFuture<TectonicPlate>> fallback = () -> getSafe(x, z)
|
||||
.exceptionally(e -> {
|
||||
if (e instanceof InterruptedException) {
|
||||
Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a thread intterruption (hotload?)");
|
||||
Iris.reportError(e);
|
||||
} else {
|
||||
Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a unknown exception");
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.thenCompose(p -> {
|
||||
release.apply(p);
|
||||
if (p != null) return CompletableFuture.completedFuture(p);
|
||||
Iris.warn("Retrying to get " + x + " " + z + " Mantle Region");
|
||||
return getFuture(x, z);
|
||||
});
|
||||
|
||||
if (!trim || !unload) {
|
||||
return getSafe(x, z)
|
||||
.thenApply(release)
|
||||
.exceptionallyCompose(e -> {
|
||||
e.printStackTrace();
|
||||
return fallback.get();
|
||||
});
|
||||
}
|
||||
|
||||
Long key = key(x, z);
|
||||
TectonicPlate p = loadedRegions.get(key);
|
||||
if (p != null && !p.isClosed()) {
|
||||
use(key);
|
||||
return CompletableFuture.completedFuture(release.apply(p));
|
||||
}
|
||||
|
||||
return fallback.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* This retreives a future of the Tectonic Plate at the given coordinates.
|
||||
* All methods accessing tectonic plates should go through this method
|
||||
@@ -563,8 +676,8 @@ public class Mantle {
|
||||
* @return the future of a tectonic plate.
|
||||
*/
|
||||
@RegionCoordinates
|
||||
private Future<TectonicPlate> getSafe(int x, int z) {
|
||||
return ioBurst.completeValue(() -> hyperLock.withResult(x, z, () -> {
|
||||
protected CompletableFuture<TectonicPlate> getSafe(int x, int z) {
|
||||
return ioBurst.completableFuture(() -> hyperLock.withResult(x, z, () -> {
|
||||
Long k = key(x, z);
|
||||
use(k);
|
||||
TectonicPlate r = loadedRegions.get(k);
|
||||
|
||||
@@ -199,16 +199,12 @@ public class MantleChunk extends FlaggedChunk {
|
||||
*/
|
||||
@ChunkCoordinates
|
||||
public Matter getOrCreate(int section) {
|
||||
Matter matter = get(section);
|
||||
final Matter matter = get(section);
|
||||
if (matter != null) return matter;
|
||||
|
||||
if (matter == null) {
|
||||
matter = new IrisMatter(16, 16, 16);
|
||||
if (!sections.compareAndSet(section, null, matter)) {
|
||||
matter = get(section);
|
||||
}
|
||||
}
|
||||
|
||||
return matter;
|
||||
final Matter instance = new IrisMatter(16, 16, 16);
|
||||
final Matter value = sections.compareAndExchange(section, null, instance);
|
||||
return value == null ? instance : value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -175,10 +175,13 @@ public class TectonicPlate {
|
||||
*/
|
||||
@ChunkCoordinates
|
||||
public MantleChunk getOrCreate(int x, int z) {
|
||||
return chunks.updateAndGet(index(x, z), chunk -> {
|
||||
if (chunk != null) return chunk;
|
||||
return new MantleChunk(sectionHeight, x & 31, z & 31);
|
||||
});
|
||||
final int index = index(x, z);
|
||||
final MantleChunk chunk = chunks.get(index);
|
||||
if (chunk != null) return chunk;
|
||||
|
||||
final MantleChunk instance = new MantleChunk(sectionHeight, x & 31, z & 31);
|
||||
final MantleChunk value = chunks.compareAndExchange(index, null, instance);
|
||||
return value == null ? instance : value;
|
||||
}
|
||||
|
||||
@ChunkCoordinates
|
||||
|
||||
@@ -172,6 +172,18 @@ public class MultiBurst implements ExecutorService {
|
||||
return getService().submit(o);
|
||||
}
|
||||
|
||||
public <T> CompletableFuture<T> completableFuture(Callable<T> o) {
|
||||
CompletableFuture<T> f = new CompletableFuture<>();
|
||||
getService().submit(() -> {
|
||||
try {
|
||||
f.complete(o.call());
|
||||
} catch (Exception e) {
|
||||
f.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
return f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
close();
|
||||
|
||||
@@ -6,14 +6,9 @@ import com.volmit.iris.engine.framework.Engine
|
||||
import com.volmit.iris.util.context.ChunkContext
|
||||
import com.volmit.iris.util.documentation.ChunkCoordinates
|
||||
import com.volmit.iris.util.mantle.Mantle
|
||||
import com.volmit.iris.util.mantle.MantleChunk
|
||||
import com.volmit.iris.util.mantle.flag.MantleFlag
|
||||
import com.volmit.iris.util.parallel.MultiBurst
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.flow.FlowCollector
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
@@ -32,45 +27,36 @@ interface MatterGenerator {
|
||||
|
||||
mantle.write(engine.mantle, x, z, radius, multicore).use { writer ->
|
||||
for (pair in components) {
|
||||
radius(x, z, pair.b, { x, z ->
|
||||
radius(x, z, pair.b) { x, z ->
|
||||
for (c in pair.a) {
|
||||
emit(Triple(x, z, c))
|
||||
}
|
||||
}, { (x, z, c) -> launch(multicore) {
|
||||
acquireChunk(multicore, writer, x, z)
|
||||
.raiseFlagSuspend(MantleFlag.PLANNED, c.flag) {
|
||||
c.generateLayer(writer, x, z, context)
|
||||
launch(multicore) {
|
||||
writer.acquireChunk(x, z)
|
||||
.raiseFlagSuspend(MantleFlag.PLANNED, c.flag) {
|
||||
c.generateLayer(writer, x, z, context)
|
||||
}
|
||||
}
|
||||
}})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
radius(x, z, realRadius, { x, z ->
|
||||
emit(Pair(x, z))
|
||||
}, {
|
||||
writer.acquireChunk(it.a, it.b)
|
||||
radius(x, z, realRadius) { x, z ->
|
||||
writer.acquireChunk(x, z)
|
||||
.flag(MantleFlag.PLANNED, true)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> radius(x: Int, z: Int, radius: Int, collector: suspend FlowCollector<T>.(Int, Int) -> Unit, task: suspend CoroutineScope.(T) -> Unit) = runBlocking {
|
||||
flow {
|
||||
for (i in -radius..radius) {
|
||||
for (j in -radius..radius) {
|
||||
collector(x + i, z + j)
|
||||
}
|
||||
private inline fun radius(x: Int, z: Int, radius: Int, crossinline task: suspend CoroutineScope.(Int, Int) -> Unit) = runBlocking {
|
||||
for (i in -radius..radius) {
|
||||
for (j in -radius..radius) {
|
||||
task(x + i, z + j)
|
||||
}
|
||||
}.collect { task(it) }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val dispatcher = MultiBurst.burst.dispatcher
|
||||
private val dispatcher = MultiBurst.burst.dispatcher//.limitedParallelism(128, "Mantle")
|
||||
private fun CoroutineScope.launch(multicore: Boolean, block: suspend CoroutineScope.() -> Unit) =
|
||||
launch(if (multicore) dispatcher else EmptyCoroutineContext, block = block)
|
||||
|
||||
private suspend fun CoroutineScope.acquireChunk(multicore: Boolean, writer: MantleWriter, x: Int, z: Int): MantleChunk {
|
||||
return if (multicore) async(Dispatchers.IO) { writer.acquireChunk(x, z) }.await()
|
||||
else writer.acquireChunk(x, z)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user