More cache improvements

This commit is contained in:
Zoe Gidiere
2024-10-11 18:27:37 -06:00
parent 819be16d83
commit 305255511d
5 changed files with 72 additions and 68 deletions
@@ -151,7 +151,7 @@ public class NoiseAddon implements AddonInitializer {
noiseRegistry.register(addon.key("MAX"), () -> new BinaryArithmeticTemplate<>(MaxSampler::new)); noiseRegistry.register(addon.key("MAX"), () -> new BinaryArithmeticTemplate<>(MaxSampler::new));
noiseRegistry.register(addon.key("MIN"), () -> new BinaryArithmeticTemplate<>(MinSampler::new)); noiseRegistry.register(addon.key("MIN"), () -> new BinaryArithmeticTemplate<>(MinSampler::new));
noiseRegistry.register(addon.key("CACHE"), () -> new CacheSamplerTemplate(plugin.getGenerationThreads())); noiseRegistry.register(addon.key("CACHE"), CacheSamplerTemplate::new);
Map<String, DimensionApplicableNoiseSampler> packSamplers = new LinkedHashMap<>(); Map<String, DimensionApplicableNoiseSampler> packSamplers = new LinkedHashMap<>();
@@ -13,14 +13,12 @@ public class CacheSamplerTemplate extends SamplerTemplate<CacheSampler> {
@Default @Default
private NoiseSampler sampler; private NoiseSampler sampler;
private final int generationThreads; public CacheSamplerTemplate() {
public CacheSamplerTemplate(int generationThreads) {
this.generationThreads = generationThreads;
} }
@Override @Override
public NoiseSampler get() { public NoiseSampler get() {
return new CacheSampler(sampler, getDimensions(), generationThreads); return new CacheSampler(sampler, getDimensions());
} }
} }
@@ -3,6 +3,10 @@ package com.dfsek.terra.addons.noise.samplers;
import com.dfsek.terra.api.noise.DerivativeNoiseSampler; import com.dfsek.terra.api.noise.DerivativeNoiseSampler;
import com.dfsek.terra.api.noise.NoiseSampler; import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.generic.pair.Pair;
import com.dfsek.terra.api.util.generic.pair.Pair.Mutable;
import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache; import com.github.benmanes.caffeine.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.Scheduler; import com.github.benmanes.caffeine.cache.Scheduler;
@@ -13,60 +17,58 @@ import static com.dfsek.terra.api.util.CacheUtils.CACHE_EXECUTOR;
public class CacheSampler implements NoiseSampler { public class CacheSampler implements NoiseSampler {
private final NoiseSampler sampler; private final NoiseSampler sampler;
private final LoadingCache<DoubleSeededVector2, Double> cache2D; private final ThreadLocal<Mutable<DoubleSeededVector2, LoadingCache<DoubleSeededVector2, Double>>> cache2D;
private final LoadingCache<DoubleSeededVector3, Double> cache3D; private final ThreadLocal<Mutable<DoubleSeededVector3, LoadingCache<DoubleSeededVector3, Double>>> cache3D;
private final ThreadLocal<DoubleSeededVector2> mutable2 = public CacheSampler(NoiseSampler sampler, int dimensions) {
ThreadLocal.withInitial(() -> new DoubleSeededVector2(0, 0, 0));
private final ThreadLocal<DoubleSeededVector3> mutable3 =
ThreadLocal.withInitial(() -> new DoubleSeededVector3(0, 0, 0, 0));
public CacheSampler(NoiseSampler sampler, int dimensions, int generationThreads) {
this.sampler = sampler; this.sampler = sampler;
int size = generationThreads * 256;
if (dimensions == 2) { if (dimensions == 2) {
this.cache2D = Caffeine LoadingCache<DoubleSeededVector2, Double> cache = Caffeine
.newBuilder() .newBuilder()
.executor(CACHE_EXECUTOR) .executor(CACHE_EXECUTOR)
.scheduler(Scheduler.systemScheduler()) .scheduler(Scheduler.systemScheduler())
.initialCapacity(size) .initialCapacity(256)
.maximumSize(size) .maximumSize(256)
.build(vec -> { .build(this::sampleNoise);
mutable2.remove(); this.cache2D = ThreadLocal.withInitial(() -> Pair.of(new DoubleSeededVector2(0, 0, 0), cache).mutable());
return this.sampler.noise(vec.seed, vec.x, vec.z); this.cache3D = null;
});
cache3D = null;
mutable3.remove();
} else { } else {
int size3D = size * 384; LoadingCache<DoubleSeededVector3, Double> cache = Caffeine
this.cache3D = Caffeine
.newBuilder() .newBuilder()
.executor(CACHE_EXECUTOR) .executor(CACHE_EXECUTOR)
.scheduler(Scheduler.systemScheduler()) .scheduler(Scheduler.systemScheduler())
.initialCapacity(size3D) .initialCapacity(981504)
.maximumSize(size3D) .maximumSize(981504)
.build(vec -> { .build(this::sampleNoise);
mutable3.remove(); this.cache3D = ThreadLocal.withInitial(() -> Pair.of(new DoubleSeededVector3(0, 0, 0, 0), cache).mutable());
return this.sampler.noise(vec.seed, vec.x, vec.y, vec.z); this.cache2D = null;
});
cache2D = null;
mutable2.remove();
} }
} }
private Double sampleNoise(DoubleSeededVector2 vec) {
this.cache2D.get().setLeft(new DoubleSeededVector2(0, 0, 0));
return this.sampler.noise(vec.seed, vec.x, vec.z);
}
private Double sampleNoise(DoubleSeededVector3 vec) {
this.cache3D.get().setLeft(new DoubleSeededVector3(0, 0, 0, 0));
return this.sampler.noise(vec.seed, vec.x, vec.z);
}
@Override @Override
public double noise(long seed, double x, double y) { public double noise(long seed, double x, double y) {
DoubleSeededVector2 mutableKey = mutable2.get(); Mutable<DoubleSeededVector2, LoadingCache<DoubleSeededVector2, Double>> cachePair = cache2D.get();
DoubleSeededVector2 mutableKey = cachePair.getLeft();
mutableKey.set(x, y, seed); mutableKey.set(x, y, seed);
return cache2D.get(mutableKey); return cachePair.getRight().get(mutableKey);
} }
@Override @Override
public double noise(long seed, double x, double y, double z) { public double noise(long seed, double x, double y, double z) {
DoubleSeededVector3 mutableKey = mutable3.get(); Mutable<DoubleSeededVector3, LoadingCache<DoubleSeededVector3, Double>> cachePair = cache3D.get();
DoubleSeededVector3 mutableKey = cachePair.getLeft();
mutableKey.set(x, y, z, seed); mutableKey.set(x, y, z, seed);
return cache3D.get(mutableKey); return cachePair.getRight().get(mutableKey);
} }
private static class DoubleSeededVector3 { private static class DoubleSeededVector3 {
@@ -97,7 +97,7 @@ public interface BiomeProvider {
if(this instanceof CachingBiomeProvider cachingBiomeProvider) { if(this instanceof CachingBiomeProvider cachingBiomeProvider) {
return cachingBiomeProvider; return cachingBiomeProvider;
} }
return new CachingBiomeProvider(this, platform.getGenerationThreads()); return new CachingBiomeProvider(this);
} }
@@ -1,5 +1,9 @@
package com.dfsek.terra.api.world.biome.generation; package com.dfsek.terra.api.world.biome.generation;
import com.dfsek.terra.api.util.generic.pair.Pair;
import com.dfsek.terra.api.util.generic.pair.Pair.Mutable;
import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache; import com.github.benmanes.caffeine.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.Scheduler; import com.github.benmanes.caffeine.cache.Scheduler;
@@ -20,47 +24,45 @@ import static com.dfsek.terra.api.util.CacheUtils.CACHE_EXECUTOR;
public class CachingBiomeProvider implements BiomeProvider, Handle { public class CachingBiomeProvider implements BiomeProvider, Handle {
protected final BiomeProvider delegate; protected final BiomeProvider delegate;
private final int res; private final int res;
private final LoadingCache<SeededVector3, Biome> cache; private final ThreadLocal<Pair.Mutable<SeededVector3, LoadingCache<SeededVector3, Biome>>> cache;
private final LoadingCache<SeededVector2, Optional<Biome>> baseCache; private final ThreadLocal<Pair.Mutable<SeededVector2, LoadingCache<SeededVector2, Optional<Biome>>>> baseCache;
private final ThreadLocal<SeededVector2> mutable2 = protected CachingBiomeProvider(BiomeProvider delegate) {
ThreadLocal.withInitial(() -> new SeededVector2(0, 0, 0));
private final ThreadLocal<SeededVector3> mutable3 =
ThreadLocal.withInitial(() -> new SeededVector3(0, 0, 0, 0));
protected CachingBiomeProvider(BiomeProvider delegate, int generationThreads) {
this.delegate = delegate; this.delegate = delegate;
this.res = delegate.resolution(); this.res = delegate.resolution();
int size = generationThreads * 256; LoadingCache<SeededVector2, Optional<Biome>> cache = Caffeine
this.baseCache = Caffeine
.newBuilder() .newBuilder()
.executor(CACHE_EXECUTOR) .executor(CACHE_EXECUTOR)
.scheduler(Scheduler.systemScheduler()) .scheduler(Scheduler.systemScheduler())
.initialCapacity(size) .initialCapacity(256)
.maximumSize(size) .maximumSize(256)
.build(vec -> { .build(this::sampleBiome);
mutable2.remove(); this.baseCache = ThreadLocal.withInitial(() -> Pair.of(new SeededVector2(0, 0, 0), cache).mutable());
return delegate.getBaseBiome(vec.x * res, vec.z * res, vec.seed);
});
int size3D = size * 384; LoadingCache<SeededVector3, Biome> cache3D = Caffeine
this.cache = Caffeine
.newBuilder() .newBuilder()
.executor(CACHE_EXECUTOR) .executor(CACHE_EXECUTOR)
.scheduler(Scheduler.systemScheduler()) .scheduler(Scheduler.systemScheduler())
.initialCapacity(size3D) .initialCapacity(981504)
.maximumSize(size3D) .maximumSize(981504)
.build(vec -> { .build(this::sampleBiome);
mutable3.remove(); this.cache = ThreadLocal.withInitial(() -> Pair.of(new SeededVector3(0, 0, 0, 0), cache3D).mutable());
return delegate.getBiome(vec.x * res, vec.y * res, vec.z * res, vec.seed);
});
} }
private Optional<Biome> sampleBiome(SeededVector2 vec) {
this.baseCache.get().setLeft(new SeededVector2(0, 0, 0));
return this.delegate.getBaseBiome(vec.x * res, vec.z * res, vec.seed);
}
private Biome sampleBiome(SeededVector3 vec) {
this.cache.get().setLeft(new SeededVector3(0, 0, 0, 0));
return this.delegate.getBiome(vec.x * res, vec.y * res, vec.z * res, vec.seed);
}
@Override @Override
public BiomeProvider getHandle() { public BiomeProvider getHandle() {
return delegate; return delegate;
@@ -68,16 +70,18 @@ public class CachingBiomeProvider implements BiomeProvider, Handle {
@Override @Override
public Biome getBiome(int x, int y, int z, long seed) { public Biome getBiome(int x, int y, int z, long seed) {
SeededVector3 mutableKey = mutable3.get(); Mutable<SeededVector3, LoadingCache<SeededVector3, Biome>> cachePair = cache.get();
SeededVector3 mutableKey = cachePair.getLeft();
mutableKey.set(x, y, z, seed); mutableKey.set(x, y, z, seed);
return cache.get(mutableKey); return cachePair.getRight().get(mutableKey);
} }
@Override @Override
public Optional<Biome> getBaseBiome(int x, int z, long seed) { public Optional<Biome> getBaseBiome(int x, int z, long seed) {
SeededVector2 mutableKey = mutable2.get(); Mutable<SeededVector2, LoadingCache<SeededVector2, Optional<Biome>>> cachePair = baseCache.get();
SeededVector2 mutableKey = cachePair.getLeft();
mutableKey.set(x, z, seed); mutableKey.set(x, z, seed);
return baseCache.get(mutableKey); return cachePair.getRight().get(mutableKey);
} }
@Override @Override