Revamped caching again, this time, without sorting.
Implemented cache changes.
This commit is contained in:
parent
91644f9ba0
commit
629660c8fc
@ -1,55 +1,61 @@
|
|||||||
package ca.recrown.islandsurvivalcraft.caching;
|
package ca.recrown.islandsurvivalcraft.caching;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.PriorityQueue;
|
import java.util.LinkedList;
|
||||||
|
|
||||||
public class Cache<Key, Value> {
|
public class Cache<Key, Value> {
|
||||||
private final int maxSize;
|
private final int maxSize;
|
||||||
private final float initialUsageFactor;
|
private final HashMap<Key, Value> data;
|
||||||
private final HashMap<Key, CacheDataSet<Key, Value>> data;
|
private final HashMap<Key, Integer> occurrences;
|
||||||
private final PriorityQueue<UsageTracker<Key>> usageTrackers;
|
private final LinkedList<Key> usageTrackers;
|
||||||
|
|
||||||
public Cache(int maxSize, float initialUsageFactor) {
|
|
||||||
data = new HashMap<>(maxSize);
|
|
||||||
usageTrackers = new PriorityQueue<>(maxSize);
|
|
||||||
this.maxSize = maxSize;
|
|
||||||
this.initialUsageFactor = initialUsageFactor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Cache(int maxSize) {
|
public Cache(int maxSize) {
|
||||||
this(maxSize, 1f);
|
data = new HashMap<>(maxSize);
|
||||||
}
|
usageTrackers = new LinkedList<>();
|
||||||
|
this.maxSize = maxSize;
|
||||||
public Cache(float initialUsageFactor) {
|
this.occurrences = new HashMap<>();
|
||||||
this(1024, initialUsageFactor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cache() {
|
public Cache() {
|
||||||
this(1024);
|
this(1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValue(Key key, Value value) {
|
public synchronized void setValue(Key key, Value value) {
|
||||||
CacheDataSet<Key, Value> dataSet = null;
|
if (!data.containsKey(key)) {
|
||||||
if (data.containsKey(key)) {
|
if (data.size() >= maxSize) {
|
||||||
dataSet = data.get(key);
|
while (occurrences.get(usageTrackers.peek()) > 1) {
|
||||||
usageTrackers.remove(dataSet.getUsageTracker());
|
occurencesTrackedPoll();
|
||||||
} else {
|
|
||||||
if (usageTrackers.size() >= maxSize) {
|
|
||||||
data.remove(usageTrackers.poll().getKey());
|
|
||||||
}
|
}
|
||||||
int currentLowest = 0;
|
data.remove(usageTrackers.poll());
|
||||||
if (!usageTrackers.isEmpty()) {
|
|
||||||
currentLowest = usageTrackers.peek().getUsage();
|
|
||||||
}
|
}
|
||||||
dataSet = new CacheDataSet<>(new UsageTracker<>(key, (int) initialUsageFactor * currentLowest));
|
occurrencesTrackedAdd(key);
|
||||||
data.put(key, dataSet);
|
|
||||||
}
|
}
|
||||||
dataSet.getUsageTracker().increaseUsage();
|
data.put(key, value);
|
||||||
usageTrackers.add(dataSet.getUsageTracker());
|
|
||||||
dataSet.setValue(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void occurrencesTrackedAdd(Key key) {
|
||||||
|
int occ = 0;
|
||||||
|
if (occurrences.get(key) != null) {
|
||||||
|
occ = occurrences.get(key);
|
||||||
|
}
|
||||||
|
occ++;
|
||||||
|
occurrences.put(key, occ);
|
||||||
|
usageTrackers.add(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Key occurencesTrackedPoll() {
|
||||||
|
Key key = usageTrackers.poll();
|
||||||
|
int occ = 0;
|
||||||
|
if (occurrences.get(key) != null) {
|
||||||
|
occ = occurrences.get(key);
|
||||||
|
}
|
||||||
|
occ--;
|
||||||
|
if (occ < 0) throw new IllegalStateException();
|
||||||
|
occurrences.put(key, occ);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves cached value associated to the key.
|
* Retrieves cached value associated to the key.
|
||||||
* Uses equals.
|
* Uses equals.
|
||||||
@ -58,21 +64,8 @@ public class Cache<Key, Value> {
|
|||||||
* @return the value associated to the key.
|
* @return the value associated to the key.
|
||||||
*/
|
*/
|
||||||
public Value getValue(Key key) {
|
public Value getValue(Key key) {
|
||||||
CacheDataSet<Key, Value> dataSet = null;
|
occurrencesTrackedAdd(key);
|
||||||
dataSet = data.get(key);
|
return data.get(key);
|
||||||
if (dataSet == null) return null;
|
|
||||||
|
|
||||||
usageTrackers.remove(dataSet.getUsageTracker());
|
|
||||||
dataSet.getUsageTracker().increaseUsage();
|
|
||||||
usageTrackers.add(dataSet.getUsageTracker());
|
|
||||||
return dataSet.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasValue(Key key) {
|
|
||||||
if (data.containsKey(key)) {
|
|
||||||
return data.get(key).getValue() != null;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearCache() {
|
public void clearCache() {
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
package ca.recrown.islandsurvivalcraft.caching;
|
|
||||||
|
|
||||||
public class CacheDataSet<Key, Value> {
|
|
||||||
private final UsageTracker<Key> usage;
|
|
||||||
private Value val;
|
|
||||||
|
|
||||||
public CacheDataSet(UsageTracker<Key> usageTracker) {
|
|
||||||
this.usage = usageTracker;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the usage tracker.
|
|
||||||
*/
|
|
||||||
public UsageTracker<Key> getUsageTracker() {
|
|
||||||
return usage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the data
|
|
||||||
*/
|
|
||||||
public Value getValue() {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param data the data to set
|
|
||||||
*/
|
|
||||||
public void setValue(Value data) {
|
|
||||||
this.val = data;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
package ca.recrown.islandsurvivalcraft.caching;
|
|
||||||
|
|
||||||
public class UsageTracker<Key> implements Comparable<UsageTracker<Key>> {
|
|
||||||
private int usage;
|
|
||||||
private final Key key;
|
|
||||||
|
|
||||||
public UsageTracker(Key key, int initialUsage) {
|
|
||||||
this.key = key;
|
|
||||||
this.usage = initialUsage;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(UsageTracker<Key> o) {
|
|
||||||
return usage - o.usage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void increaseUsage() {
|
|
||||||
usage++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the usage
|
|
||||||
*/
|
|
||||||
public int getUsage() {
|
|
||||||
return usage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the key
|
|
||||||
*/
|
|
||||||
public Key getKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,11 @@
|
|||||||
package ca.recrown.islandsurvivalcraft.world.generation;
|
package ca.recrown.islandsurvivalcraft.world.generation;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Biome;
|
import org.bukkit.block.Biome;
|
||||||
|
|
||||||
|
import ca.recrown.islandsurvivalcraft.Utilities;
|
||||||
import ca.recrown.islandsurvivalcraft.Types.Point2;
|
import ca.recrown.islandsurvivalcraft.Types.Point2;
|
||||||
import ca.recrown.islandsurvivalcraft.caching.Cache;
|
import ca.recrown.islandsurvivalcraft.caching.Cache;
|
||||||
import ca.recrown.islandsurvivalcraft.pathfinding.CoordinateTargetValidatable;
|
import ca.recrown.islandsurvivalcraft.pathfinding.CoordinateTargetValidatable;
|
||||||
@ -31,13 +34,14 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
|
|||||||
|
|
||||||
public BiomePerIslandGenerator() {
|
public BiomePerIslandGenerator() {
|
||||||
this.temperatureMapGenerator = new TemperatureMapGenerator();
|
this.temperatureMapGenerator = new TemperatureMapGenerator();
|
||||||
chunkBiomesCache = new Cache<>(1024, 1.5f);
|
chunkBiomesCache = new Cache<>(1024);
|
||||||
chunkGenStatusCache = new Cache<>(1024);
|
chunkGenStatusCache = new Cache<>(1024);
|
||||||
freshCachePropInfo = new FreshCachePropagationInfo();
|
freshCachePropInfo = new FreshCachePropagationInfo();
|
||||||
freshCachePropagator = new DepthFirstSearch(freshCachePropInfo);
|
freshCachePropagator = new DepthFirstSearch(freshCachePropInfo);
|
||||||
existenceInfo = new PreviousGenerationInfo();
|
existenceInfo = new PreviousGenerationInfo();
|
||||||
existenceChecker = new DepthFirstSearch(existenceInfo);
|
existenceChecker = new DepthFirstSearch(existenceInfo);
|
||||||
localChunkCache = new Biome[16][16];
|
localChunkCache = new Biome[16][16];
|
||||||
|
currChunkCoords = new Point2();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -57,17 +61,20 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Biome GenerateBiome(int chunkX, int chunkZ, int localX, int localZ) {
|
public Biome GenerateBiome(int chunkX, int chunkZ, int localX, int localZ) {
|
||||||
if (currChunkCoords == null || chunkX != currChunkCoords.x || chunkZ != currChunkCoords.y) {
|
if (chunkX != currChunkCoords.x || chunkZ != currChunkCoords.y) {
|
||||||
currChunkCoords.x = chunkX;
|
currChunkCoords.x = chunkX;
|
||||||
currChunkCoords.y = chunkZ;
|
currChunkCoords.y = chunkZ;
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
Arrays.fill(localChunkCache[i], null);
|
||||||
}
|
}
|
||||||
int worldX = localX + 16 * chunkX;
|
}
|
||||||
int worldZ = localZ + 16 * chunkZ;
|
int worldX = Utilities.addMagnitude(16 * chunkX, localX);
|
||||||
|
int worldZ = Utilities.addMagnitude(16 * chunkZ, localZ);
|
||||||
|
|
||||||
Biome cachedBiome = getBiome(worldX, worldZ);
|
Biome cachedBiome = getBiome(worldX, worldZ);
|
||||||
if (cachedBiome != null) return cachedBiome;
|
if (cachedBiome != null) return cachedBiome;
|
||||||
temperature = temperatureMapGenerator.getTemperature(worldX, worldZ);
|
|
||||||
|
|
||||||
|
temperature = temperatureMapGenerator.getTemperature(worldX, worldZ);
|
||||||
if (!worldIslandMap.isIsland(worldX, worldZ)) {
|
if (!worldIslandMap.isIsland(worldX, worldZ)) {
|
||||||
return biomeSelector.getOceanBiome(temperature);
|
return biomeSelector.getOceanBiome(temperature);
|
||||||
}
|
}
|
||||||
@ -81,7 +88,7 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
|
|||||||
}
|
}
|
||||||
freshCachePropagator.setStartPosition(worldX, worldZ);
|
freshCachePropagator.setStartPosition(worldX, worldZ);
|
||||||
freshCachePropagator.findTarget(freshCachePropInfo);
|
freshCachePropagator.findTarget(freshCachePropInfo);
|
||||||
return localChunkCache[localX][localZ];
|
return getBiome(worldX, worldZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Biome getBiome(int worldX, int worldZ) {
|
private Biome getBiome(int worldX, int worldZ) {
|
||||||
@ -89,6 +96,10 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
|
|||||||
int localZ = Math.abs(worldZ % 16);
|
int localZ = Math.abs(worldZ % 16);
|
||||||
Point2 chunkCoords = new Point2(worldX / 16, worldZ / 16);
|
Point2 chunkCoords = new Point2(worldX / 16, worldZ / 16);
|
||||||
|
|
||||||
|
if (chunkCoords.equals(this.currChunkCoords) && localChunkCache[localX][localZ] != null) {
|
||||||
|
return localChunkCache[localX][localZ];
|
||||||
|
}
|
||||||
|
|
||||||
Biome[][] biomes = chunkBiomesCache.getValue(chunkCoords);
|
Biome[][] biomes = chunkBiomesCache.getValue(chunkCoords);
|
||||||
|
|
||||||
if (biomes != null) {
|
if (biomes != null) {
|
||||||
@ -98,7 +109,8 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
|
|||||||
|
|
||||||
Boolean chunkGenStat = chunkGenStatusCache.getValue(chunkCoords);
|
Boolean chunkGenStat = chunkGenStatusCache.getValue(chunkCoords);
|
||||||
if (chunkGenStat == null) {
|
if (chunkGenStat == null) {
|
||||||
chunkGenStatusCache.setValue(chunkCoords, world.isChunkGenerated(chunkCoords.x, chunkCoords.y));
|
chunkGenStat = world.isChunkGenerated(chunkCoords.x, chunkCoords.y);
|
||||||
|
chunkGenStatusCache.setValue(chunkCoords, chunkGenStat);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chunkGenStat) {
|
if (chunkGenStat) {
|
||||||
@ -119,7 +131,7 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
|
|||||||
|
|
||||||
Biome[][] chunkBiomes = chunkBiomesCache.getValue(chunkCoords);
|
Biome[][] chunkBiomes = chunkBiomesCache.getValue(chunkCoords);
|
||||||
if (chunkBiomes == null) chunkBiomes = new Biome[16][16];
|
if (chunkBiomes == null) chunkBiomes = new Biome[16][16];
|
||||||
|
chunkBiomes[localX][localZ] = biome;
|
||||||
chunkBiomesCache.setValue(chunkCoords, chunkBiomes);
|
chunkBiomesCache.setValue(chunkCoords, chunkBiomes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +156,8 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean validate(int x, int y) {
|
public boolean validate(int x, int y) {
|
||||||
return x / 16 == currChunkCoords.x && y / 16 == currChunkCoords.y && worldIslandMap.isIsland(x, y);
|
Point2 chunkCoords = new Point2(x / 16, y / 16);
|
||||||
|
return chunkCoords.x == currChunkCoords.x && chunkCoords.y == currChunkCoords.y && worldIslandMap.isIsland(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean allBiomesAcquired() {
|
public boolean allBiomesAcquired() {
|
||||||
|
@ -31,7 +31,10 @@ class TemperatureMapGenerator {
|
|||||||
public float getTemperature(int worldX, int worldZ) {
|
public float getTemperature(int worldX, int worldZ) {
|
||||||
Point2 loc = new Point2(worldX/4, worldZ/4);
|
Point2 loc = new Point2(worldX/4, worldZ/4);
|
||||||
Float val = temperatureCache.getValue(loc);
|
Float val = temperatureCache.getValue(loc);
|
||||||
if (val == null) temperatureCache.setValue(loc, (float) noiseGenerator.noise(worldX/4, worldZ/4, frequency, amplitude, true));
|
if (val == null) {
|
||||||
|
val = (float) noiseGenerator.noise(worldX/4, worldZ/4, frequency, amplitude, true);
|
||||||
|
temperatureCache.setValue(loc, val);
|
||||||
|
}
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,9 +2,10 @@ package ca.recrown.islandsurvivalcraft.caching;
|
|||||||
|
|
||||||
import static org.junit.Assert.assertSame;
|
import static org.junit.Assert.assertSame;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -31,14 +32,14 @@ public class CacheTest {
|
|||||||
assertTrue(val == null);
|
assertTrue(val == null);
|
||||||
val = "first";
|
val = "first";
|
||||||
integerCache.setValue(0, val);
|
integerCache.setValue(0, val);
|
||||||
assertTrue(integerCache.hasValue(0));
|
assertTrue(integerCache.getValue(0) != null);
|
||||||
assertSame(val, integerCache.getValue(0));
|
assertSame(val, integerCache.getValue(0));
|
||||||
|
|
||||||
val = integerCache.getValue(1);
|
val = integerCache.getValue(1);
|
||||||
assertTrue(val == null);
|
assertTrue(val == null);
|
||||||
val = "second";
|
val = "second";
|
||||||
integerCache.setValue(1, val);
|
integerCache.setValue(1, val);
|
||||||
assertTrue(integerCache.hasValue(1));
|
assertTrue(integerCache.getValue(1) != null);
|
||||||
assertSame(val, integerCache.getValue(1));
|
assertSame(val, integerCache.getValue(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,11 +52,57 @@ public class CacheTest {
|
|||||||
assertEquals("first", integerCache.getValue(0));
|
assertEquals("first", integerCache.getValue(0));
|
||||||
|
|
||||||
integerCache.setValue(3, "fourth");
|
integerCache.setValue(3, "fourth");
|
||||||
|
|
||||||
assertEquals("first", integerCache.getValue(0));
|
assertEquals("first", integerCache.getValue(0));
|
||||||
assertTrue(integerCache.hasValue(1));
|
|
||||||
|
|
||||||
integerCache.setValue(5, "sixth");
|
integerCache.setValue(4, "fifth");
|
||||||
assertFalse(integerCache.hasValue(2));
|
assertTrue(integerCache.getValue(3) != null);
|
||||||
|
assertTrue(integerCache.getValue(0) != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUsageLargeData() {
|
||||||
|
int amount = 1024;
|
||||||
|
Random random = new Random();
|
||||||
|
|
||||||
|
Cache<Integer, Integer> largeCache = new Cache<>(amount/2);
|
||||||
|
|
||||||
|
int[] expected = new int[amount];
|
||||||
|
for (int i = 0; i < amount; i++) {
|
||||||
|
expected[i] = random.nextInt();
|
||||||
|
largeCache.setValue(i, expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < amount /2; i++) {
|
||||||
|
assertEquals(null, largeCache.getValue(i), "Current accessor: " + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = amount /2; i < amount; i++) {
|
||||||
|
assertEquals(expected[i], largeCache.getValue(i), "Current accessor: " + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUsageLargeDataImportance() {
|
||||||
|
int amount = 1024;
|
||||||
|
Random random = new Random();
|
||||||
|
|
||||||
|
Cache<Integer, Integer> largeCache = new Cache<>(amount/2);
|
||||||
|
|
||||||
|
int[] expected = new int[amount];
|
||||||
|
for (int i = 0; i < amount; i++) {
|
||||||
|
expected[i] = random.nextInt();
|
||||||
|
largeCache.setValue(i, expected[i]);
|
||||||
|
largeCache.getValue(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < (amount/2) + 1; i++) {
|
||||||
|
assertEquals(null, largeCache.getValue(i), "Current accessor: " + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = (amount /2) + 1; i < amount; i++) {
|
||||||
|
assertEquals(expected[i], largeCache.getValue(i), "Current accessor: " + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(expected[0], largeCache.getValue(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user