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;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class Cache<Key, Value> {
|
||||
private final int maxSize;
|
||||
private final float initialUsageFactor;
|
||||
private final HashMap<Key, CacheDataSet<Key, Value>> data;
|
||||
private final PriorityQueue<UsageTracker<Key>> usageTrackers;
|
||||
|
||||
public Cache(int maxSize, float initialUsageFactor) {
|
||||
data = new HashMap<>(maxSize);
|
||||
usageTrackers = new PriorityQueue<>(maxSize);
|
||||
this.maxSize = maxSize;
|
||||
this.initialUsageFactor = initialUsageFactor;
|
||||
}
|
||||
private final HashMap<Key, Value> data;
|
||||
private final HashMap<Key, Integer> occurrences;
|
||||
private final LinkedList<Key> usageTrackers;
|
||||
|
||||
public Cache(int maxSize) {
|
||||
this(maxSize, 1f);
|
||||
}
|
||||
|
||||
public Cache(float initialUsageFactor) {
|
||||
this(1024, initialUsageFactor);
|
||||
data = new HashMap<>(maxSize);
|
||||
usageTrackers = new LinkedList<>();
|
||||
this.maxSize = maxSize;
|
||||
this.occurrences = new HashMap<>();
|
||||
}
|
||||
|
||||
public Cache() {
|
||||
this(1024);
|
||||
}
|
||||
|
||||
public void setValue(Key key, Value value) {
|
||||
CacheDataSet<Key, Value> dataSet = null;
|
||||
if (data.containsKey(key)) {
|
||||
dataSet = data.get(key);
|
||||
usageTrackers.remove(dataSet.getUsageTracker());
|
||||
} else {
|
||||
if (usageTrackers.size() >= maxSize) {
|
||||
data.remove(usageTrackers.poll().getKey());
|
||||
public synchronized void setValue(Key key, Value value) {
|
||||
if (!data.containsKey(key)) {
|
||||
if (data.size() >= maxSize) {
|
||||
while (occurrences.get(usageTrackers.peek()) > 1) {
|
||||
occurencesTrackedPoll();
|
||||
}
|
||||
data.remove(usageTrackers.poll());
|
||||
}
|
||||
int currentLowest = 0;
|
||||
if (!usageTrackers.isEmpty()) {
|
||||
currentLowest = usageTrackers.peek().getUsage();
|
||||
}
|
||||
dataSet = new CacheDataSet<>(new UsageTracker<>(key, (int) initialUsageFactor * currentLowest));
|
||||
data.put(key, dataSet);
|
||||
occurrencesTrackedAdd(key);
|
||||
}
|
||||
dataSet.getUsageTracker().increaseUsage();
|
||||
usageTrackers.add(dataSet.getUsageTracker());
|
||||
dataSet.setValue(value);
|
||||
data.put(key, 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.
|
||||
* Uses equals.
|
||||
@ -58,21 +64,8 @@ public class Cache<Key, Value> {
|
||||
* @return the value associated to the key.
|
||||
*/
|
||||
public Value getValue(Key key) {
|
||||
CacheDataSet<Key, Value> dataSet = null;
|
||||
dataSet = 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;
|
||||
occurrencesTrackedAdd(key);
|
||||
return data.get(key);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.Utilities;
|
||||
import ca.recrown.islandsurvivalcraft.Types.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.caching.Cache;
|
||||
import ca.recrown.islandsurvivalcraft.pathfinding.CoordinateTargetValidatable;
|
||||
@ -31,13 +34,14 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
|
||||
|
||||
public BiomePerIslandGenerator() {
|
||||
this.temperatureMapGenerator = new TemperatureMapGenerator();
|
||||
chunkBiomesCache = new Cache<>(1024, 1.5f);
|
||||
chunkBiomesCache = new Cache<>(1024);
|
||||
chunkGenStatusCache = new Cache<>(1024);
|
||||
freshCachePropInfo = new FreshCachePropagationInfo();
|
||||
freshCachePropagator = new DepthFirstSearch(freshCachePropInfo);
|
||||
existenceInfo = new PreviousGenerationInfo();
|
||||
existenceChecker = new DepthFirstSearch(existenceInfo);
|
||||
localChunkCache = new Biome[16][16];
|
||||
currChunkCoords = new Point2();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -57,17 +61,20 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
|
||||
|
||||
@Override
|
||||
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.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);
|
||||
if (cachedBiome != null) return cachedBiome;
|
||||
|
||||
temperature = temperatureMapGenerator.getTemperature(worldX, worldZ);
|
||||
|
||||
if (!worldIslandMap.isIsland(worldX, worldZ)) {
|
||||
return biomeSelector.getOceanBiome(temperature);
|
||||
}
|
||||
@ -81,7 +88,7 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
|
||||
}
|
||||
freshCachePropagator.setStartPosition(worldX, worldZ);
|
||||
freshCachePropagator.findTarget(freshCachePropInfo);
|
||||
return localChunkCache[localX][localZ];
|
||||
return getBiome(worldX, worldZ);
|
||||
}
|
||||
|
||||
private Biome getBiome(int worldX, int worldZ) {
|
||||
@ -89,6 +96,10 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
|
||||
int localZ = Math.abs(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);
|
||||
|
||||
if (biomes != null) {
|
||||
@ -98,7 +109,8 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
|
||||
|
||||
Boolean chunkGenStat = chunkGenStatusCache.getValue(chunkCoords);
|
||||
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) {
|
||||
@ -119,7 +131,7 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
|
||||
|
||||
Biome[][] chunkBiomes = chunkBiomesCache.getValue(chunkCoords);
|
||||
if (chunkBiomes == null) chunkBiomes = new Biome[16][16];
|
||||
|
||||
chunkBiomes[localX][localZ] = biome;
|
||||
chunkBiomesCache.setValue(chunkCoords, chunkBiomes);
|
||||
}
|
||||
|
||||
@ -144,7 +156,8 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
|
||||
|
||||
@Override
|
||||
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() {
|
||||
|
@ -31,7 +31,10 @@ class TemperatureMapGenerator {
|
||||
public float getTemperature(int worldX, int worldZ) {
|
||||
Point2 loc = new Point2(worldX/4, worldZ/4);
|
||||
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;
|
||||
}
|
||||
}
|
@ -2,9 +2,10 @@ package ca.recrown.islandsurvivalcraft.caching;
|
||||
|
||||
import static org.junit.Assert.assertSame;
|
||||
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 java.util.Random;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -31,14 +32,14 @@ public class CacheTest {
|
||||
assertTrue(val == null);
|
||||
val = "first";
|
||||
integerCache.setValue(0, val);
|
||||
assertTrue(integerCache.hasValue(0));
|
||||
assertTrue(integerCache.getValue(0) != null);
|
||||
assertSame(val, integerCache.getValue(0));
|
||||
|
||||
val = integerCache.getValue(1);
|
||||
assertTrue(val == null);
|
||||
val = "second";
|
||||
integerCache.setValue(1, val);
|
||||
assertTrue(integerCache.hasValue(1));
|
||||
assertTrue(integerCache.getValue(1) != null);
|
||||
assertSame(val, integerCache.getValue(1));
|
||||
}
|
||||
|
||||
@ -51,11 +52,57 @@ public class CacheTest {
|
||||
assertEquals("first", integerCache.getValue(0));
|
||||
|
||||
integerCache.setValue(3, "fourth");
|
||||
|
||||
assertEquals("first", integerCache.getValue(0));
|
||||
assertTrue(integerCache.hasValue(1));
|
||||
|
||||
integerCache.setValue(5, "sixth");
|
||||
assertFalse(integerCache.hasValue(2));
|
||||
integerCache.setValue(4, "fifth");
|
||||
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