Reworked cache, this time with less hashmap accesses.

This commit is contained in:
Harrison Deng 2020-04-25 22:32:25 -05:00
parent dcec9fd7e7
commit f20515fd45
3 changed files with 68 additions and 54 deletions

View File

@ -1,59 +1,38 @@
package ca.recrown.islandsurvivalcraft.caching; package ca.recrown.islandsurvivalcraft.caching;
import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.LinkedList; import java.util.concurrent.ConcurrentLinkedQueue;
public class Cache<Key, Value> { public class Cache<Key, Value> {
private final int maxSize; private final int maxSize;
private final HashMap<Key, Value> data; private final ConcurrentHashMap<Key, CacheValue<Value>> data;
private final HashMap<Key, Integer> occurrences; private final ConcurrentLinkedQueue<Key> occurrenceOrder;
private final LinkedList<Key> occurrenceOrder;
public Cache(int maxSize) { public Cache(int maxSize) {
data = new HashMap<>(maxSize); data = new ConcurrentHashMap<>(maxSize);
occurrenceOrder = new LinkedList<>(); occurrenceOrder = new ConcurrentLinkedQueue<>();
this.maxSize = maxSize; this.maxSize = maxSize;
this.occurrences = new HashMap<>();
} }
public Cache() { public Cache() {
this(1024); this(1024);
} }
public synchronized void setValue(Key key, Value value) { public void setValue(Key key, Value value) {
if (!data.containsKey(key)) { data.put(key, new CacheValue<>(value));
if (data.size() >= maxSize) {
while (occurrences.get(occurrenceOrder.peek()) > 1) {
occurencesTrackedPoll();
}
data.remove(occurencesTrackedPoll());
}
occurrencesTrackedAdd(key);
}
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);
occurrenceOrder.add(key); occurrenceOrder.add(key);
} if (data.size() > maxSize) {
int occ = 0;
private Key occurencesTrackedPoll() { do {
Key key = occurrenceOrder.poll(); Key potentialKey = occurrenceOrder.poll();
int occ = 0; CacheValue<Value> potential = data.get(potentialKey);
if (occurrences.get(key) != null) { potential.decreaseOccurence();
occ = occurrences.get(key); occ = potential.getOccurrence();
if (occ < 1) {
data.remove(potentialKey);
}
} while (occ > 0);
} }
occ--;
if (occ < 0) throw new IllegalStateException();
occurrences.put(key, occ);
return key;
} }
/** /**
@ -63,14 +42,17 @@ public class Cache<Key, Value> {
* @param key the key associated with the value. * @param key the key associated with the value.
* @return the value associated to the key. * @return the value associated to the key.
*/ */
public synchronized Value getValue(Key key) { public Value getValue(Key key) {
occurrencesTrackedAdd(key); CacheValue<Value> res = data.get(key);
return data.get(key); if (res == null) return null;
res.increaseOccurrence();
occurrenceOrder.add(key);
return res.getValue();
} }
public synchronized void clearCache() { public void clearCache() {
data.clear(); data.clear();
occurrenceOrder.clear(); occurrenceOrder.clear();
occurrences.clear();
} }
} }

View File

@ -0,0 +1,32 @@
package ca.recrown.islandsurvivalcraft.caching;
public class CacheValue <ValueType> {
private int occurrence = 1;
private final ValueType value;
/**
* @return the occurrence
*/
public int getOccurrence() {
return occurrence;
}
public void increaseOccurrence() {
occurrence++;
}
public void decreaseOccurence() {
occurrence--;
}
public CacheValue(ValueType value) {
this.value = value;
}
/**
* @return the value
*/
public ValueType getValue() {
return value;
}
}

View File

@ -66,10 +66,10 @@ public class CacheTest {
Cache<Integer, Integer> largeCache = new Cache<>(amount/2); Cache<Integer, Integer> largeCache = new Cache<>(amount/2);
int[] expected = new int[amount]; int[] values = new int[amount];
for (int i = 0; i < amount; i++) { for (int i = 0; i < amount; i++) {
expected[i] = random.nextInt(); values[i] = random.nextInt();
largeCache.setValue(i, expected[i]); largeCache.setValue(i, values[i]);
} }
for (int i = 0; i < amount /2; i++) { for (int i = 0; i < amount /2; i++) {
@ -77,7 +77,7 @@ public class CacheTest {
} }
for (int i = amount /2; i < amount; i++) { for (int i = amount /2; i < amount; i++) {
assertEquals(expected[i], largeCache.getValue(i), "Current accessor: " + i); assertEquals(values[i], largeCache.getValue(i), "Current accessor: " + i);
} }
} }
@ -88,10 +88,10 @@ public class CacheTest {
Cache<Integer, Integer> largeCache = new Cache<>(amount/2); Cache<Integer, Integer> largeCache = new Cache<>(amount/2);
int[] expected = new int[amount]; int[] values = new int[amount];
for (int i = 0; i < amount; i++) { for (int i = 0; i < amount; i++) {
expected[i] = random.nextInt(); values[i] = random.nextInt();
largeCache.setValue(i, expected[i]); largeCache.setValue(i, values[i]);
largeCache.getValue(0); largeCache.getValue(0);
} }
@ -100,9 +100,9 @@ public class CacheTest {
} }
for (int i = (amount /2) + 1; i < amount; i++) { for (int i = (amount /2) + 1; i < amount; i++) {
assertEquals(expected[i], largeCache.getValue(i), "Current accessor: " + i); assertEquals(values[i], largeCache.getValue(i), "Current accessor: " + i);
} }
assertEquals(expected[0], largeCache.getValue(0)); assertEquals(values[0], largeCache.getValue(0));
} }
} }