From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: violetc <58360096+s-yh-china@users.noreply.github.com> Date: Thu, 18 Aug 2022 16:53:45 +0800 Subject: [PATCH] Use aging cache for biome temperatures This patch is Powered by Pufferfish(https://github.com/pufferfish-gg/Pufferfish) diff --git a/src/main/java/net/minecraft/world/level/biome/Biome.java b/src/main/java/net/minecraft/world/level/biome/Biome.java index 65012a12e1430956ef55ced56773e6354ac26444..fe43432da26a3570c993b5592b7b8020331bdb74 100644 --- a/src/main/java/net/minecraft/world/level/biome/Biome.java +++ b/src/main/java/net/minecraft/world/level/biome/Biome.java @@ -66,23 +66,37 @@ public final class Biome { private final BiomeGenerationSettings generationSettings; private final MobSpawnSettings mobSettings; private final BiomeSpecialEffects specialEffects; - private final ThreadLocal temperatureCache = ThreadLocal.withInitial(() -> { - return Util.make(() -> { - Long2FloatLinkedOpenHashMap long2FloatLinkedOpenHashMap = new Long2FloatLinkedOpenHashMap(1024, 0.25F) { - protected void rehash(int i) { - } - }; - long2FloatLinkedOpenHashMap.defaultReturnValue(Float.NaN); - return long2FloatLinkedOpenHashMap; - }); - }); + // Leaves start - use our cache + private final ThreadLocal temperatureCache; + private final ThreadLocal temperatureAgingCache; Biome(Biome.ClimateSettings weather, BiomeSpecialEffects effects, BiomeGenerationSettings generationSettings, MobSpawnSettings spawnSettings) { this.climateSettings = weather; this.generationSettings = generationSettings; this.mobSettings = spawnSettings; this.specialEffects = effects; + if (top.leavesmc.leaves.LeavesConfig.biomeTemperaturesUseAgingCache) { + temperatureCache = null; + temperatureAgingCache = ThreadLocal.withInitial(() -> { + return Util.make(() -> { + return new top.leavesmc.leaves.structs.Long2FloatAgingCache(TEMPERATURE_CACHE_SIZE); + }); + }); + } else { + temperatureCache = ThreadLocal.withInitial(() -> { + return Util.make(() -> { + Long2FloatLinkedOpenHashMap long2FloatLinkedOpenHashMap = new Long2FloatLinkedOpenHashMap(1024, 0.25F) { + protected void rehash(int i) { + } + }; + long2FloatLinkedOpenHashMap.defaultReturnValue(Float.NaN); + return long2FloatLinkedOpenHashMap; + }); + }); + temperatureAgingCache = null; + } } + // Leaves end - use our cache public int getSkyColor() { return this.specialEffects.getSkyColor(); @@ -118,19 +132,33 @@ public final class Biome { @Deprecated public float getTemperature(BlockPos blockPos) { long l = blockPos.asLong(); - Long2FloatLinkedOpenHashMap long2FloatLinkedOpenHashMap = this.temperatureCache.get(); - float f = long2FloatLinkedOpenHashMap.get(l); - if (!Float.isNaN(f)) { - return f; - } else { - float g = this.getHeightAdjustedTemperature(blockPos); - if (long2FloatLinkedOpenHashMap.size() == 1024) { - long2FloatLinkedOpenHashMap.removeFirstFloat(); + // Leaves start - use our cache + if (top.leavesmc.leaves.LeavesConfig.biomeTemperaturesUseAgingCache) { + top.leavesmc.leaves.structs.Long2FloatAgingCache cache = this.temperatureAgingCache.get(); + float f = cache.getValue(l); + if (!Float.isNaN(f)) { + return f; + } else { + float g = this.getHeightAdjustedTemperature(blockPos); + cache.putValue(l, g); + return g; } + } else { + Long2FloatLinkedOpenHashMap long2FloatLinkedOpenHashMap = this.temperatureCache.get(); + float f = long2FloatLinkedOpenHashMap.get(l); + if (!Float.isNaN(f)) { + return f; + } else { + float g = this.getHeightAdjustedTemperature(blockPos); + if (long2FloatLinkedOpenHashMap.size() == 1024) { + long2FloatLinkedOpenHashMap.removeFirstFloat(); + } - long2FloatLinkedOpenHashMap.put(l, g); - return g; + long2FloatLinkedOpenHashMap.put(l, g); + return g; + } } + // Leaves end - use our cache } public boolean shouldFreeze(LevelReader world, BlockPos blockPos) { diff --git a/src/main/java/top/leavesmc/leaves/structs/Long2FloatAgingCache.java b/src/main/java/top/leavesmc/leaves/structs/Long2FloatAgingCache.java new file mode 100644 index 0000000000000000000000000000000000000000..26f9d4d9099739d6ddc159184a20e2ae9abde5cd --- /dev/null +++ b/src/main/java/top/leavesmc/leaves/structs/Long2FloatAgingCache.java @@ -0,0 +1,121 @@ +package top.leavesmc.leaves.structs; + +import it.unimi.dsi.fastutil.HashCommon; + +// Powered by Pufferfish(https://github.com/pufferfish-gg/Pufferfish) + +/** + * A replacement for the cache used in Biome. + */ +public class Long2FloatAgingCache { + + private static class AgingEntry { + private long data; + private float value; + private int uses = 0; + private int age = 0; + + private AgingEntry(long data, float value) { + this.data = data; + this.value = value; + } + + public void replace(long data, float flag) { + this.data = data; + this.value = flag; + } + + public int getValue() { + return this.uses - (this.age >> 1); // age isn't as important as uses + } + + public void incrementUses() { + this.uses = this.uses + 1 & Integer.MAX_VALUE; + } + + public void incrementAge() { + this.age = this.age + 1 & Integer.MAX_VALUE; + } + } + + private final AgingEntry[] entries; + private final int mask; + private final int maxDistance; // the most amount of entries to check for a value + + public Long2FloatAgingCache(int size) { + int arraySize = HashCommon.nextPowerOfTwo(size); + this.entries = new AgingEntry[arraySize]; + this.mask = arraySize - 1; + this.maxDistance = Math.min(arraySize, 4); + } + + public float getValue(long data) { + AgingEntry curr; + int pos; + + if ((curr = this.entries[pos = HashCommon.mix(HashCommon.long2int(data)) & this.mask]) == null) { + return Float.NaN; + } else if (data == curr.data) { + curr.incrementUses(); + return curr.value; + } + + int checked = 1; // start at 1 because we already checked the first spot above + + while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) { + if (data == curr.data) { + curr.incrementUses(); + return curr.value; + } else if (++checked >= this.maxDistance) { + break; + } + } + + return Float.NaN; + } + + public void putValue(long data, float value) { + AgingEntry curr; + int pos; + + if ((curr = this.entries[pos = HashCommon.mix(HashCommon.long2int(data)) & this.mask]) == null) { + this.entries[pos] = new AgingEntry(data, value); // add + return; + } else if (data == curr.data) { + curr.incrementUses(); + return; + } + + int checked = 1; // start at 1 because we already checked the first spot above + + while ((curr = this.entries[pos = (pos + 1) & this.mask]) != null) { + if (data == curr.data) { + curr.incrementUses(); + return; + } else if (++checked >= this.maxDistance) { + this.forceAdd(data, value); + return; + } + } + + this.entries[pos] = new AgingEntry(data, value); // add + } + + private void forceAdd(long data, float value) { + int expectedPos = HashCommon.mix(HashCommon.long2int(data)) & this.mask; + AgingEntry entryToRemove = this.entries[expectedPos]; + + for (int i = expectedPos + 1; i < expectedPos + this.maxDistance; i++) { + int pos = i & this.mask; + AgingEntry entry = this.entries[pos]; + if (entry.getValue() < entryToRemove.getValue()) { + entryToRemove = entry; + } + + entry.incrementAge(); // use this as a mechanism to age the other entries + } + + // remove the least used/oldest entry + entryToRemove.replace(data, value); + } +}