提交 33a080ef authored 作者: Andrei Tokar's avatar Andrei Tokar

CacheLongKeyLIRS: some re-factoring,

make high water mark for non-resident queue size configurable
上级 dc6b54b1
...@@ -57,6 +57,7 @@ public class CacheLongKeyLIRS<V> { ...@@ -57,6 +57,7 @@ public class CacheLongKeyLIRS<V> {
private final int segmentMask; private final int segmentMask;
private final int stackMoveDistance; private final int stackMoveDistance;
private final int nonResidentQueueSize; private final int nonResidentQueueSize;
private final int nonResidentQueueSizeHigh;
/** /**
* Create a new cache with the given memory size. * Create a new cache with the given memory size.
...@@ -67,6 +68,7 @@ public class CacheLongKeyLIRS<V> { ...@@ -67,6 +68,7 @@ public class CacheLongKeyLIRS<V> {
public CacheLongKeyLIRS(Config config) { public CacheLongKeyLIRS(Config config) {
setMaxMemory(config.maxMemory); setMaxMemory(config.maxMemory);
this.nonResidentQueueSize = config.nonResidentQueueSize; this.nonResidentQueueSize = config.nonResidentQueueSize;
this.nonResidentQueueSizeHigh = config.nonResidentQueueSizeHigh;
DataUtils.checkArgument( DataUtils.checkArgument(
Integer.bitCount(config.segmentCount) == 1, Integer.bitCount(config.segmentCount) == 1,
"The segment count must be a power of 2, is {0}", config.segmentCount); "The segment count must be a power of 2, is {0}", config.segmentCount);
...@@ -85,8 +87,8 @@ public class CacheLongKeyLIRS<V> { ...@@ -85,8 +87,8 @@ public class CacheLongKeyLIRS<V> {
public void clear() { public void clear() {
long max = getMaxItemSize(); long max = getMaxItemSize();
for (int i = 0; i < segmentCount; i++) { for (int i = 0; i < segmentCount; i++) {
segments[i] = new Segment<>( segments[i] = new Segment<>(max, stackMoveDistance, 8, nonResidentQueueSize,
max, stackMoveDistance, 8, nonResidentQueueSize); nonResidentQueueSizeHigh);
} }
} }
...@@ -111,8 +113,8 @@ public class CacheLongKeyLIRS<V> { ...@@ -111,8 +113,8 @@ public class CacheLongKeyLIRS<V> {
* @return true if there is a resident entry * @return true if there is a resident entry
*/ */
public boolean containsKey(long key) { public boolean containsKey(long key) {
int hash = getHash(key); Entry<V> e = find(key);
return getSegment(hash).containsKey(key, hash); return e != null && e.value != null;
} }
/** /**
...@@ -149,6 +151,10 @@ public class CacheLongKeyLIRS<V> { ...@@ -149,6 +151,10 @@ public class CacheLongKeyLIRS<V> {
* @return the old value, or null if there was no resident entry * @return the old value, or null if there was no resident entry
*/ */
public V put(long key, V value, int memory) { public V put(long key, V value, int memory) {
if (value == null) {
throw DataUtils.newIllegalArgumentException(
"The value may not be null");
}
int hash = getHash(key); int hash = getHash(key);
int segmentIndex = getSegmentIndex(hash); int segmentIndex = getSegmentIndex(hash);
Segment<V> s = segments[segmentIndex]; Segment<V> s = segments[segmentIndex];
...@@ -215,8 +221,8 @@ public class CacheLongKeyLIRS<V> { ...@@ -215,8 +221,8 @@ public class CacheLongKeyLIRS<V> {
* @return the memory, or 0 if there is no resident entry * @return the memory, or 0 if there is no resident entry
*/ */
public int getMemory(long key) { public int getMemory(long key) {
int hash = getHash(key); Entry<V> e = find(key);
return getSegment(hash).getMemory(key, hash); return e == null ? 0 : e.getMemory();
} }
/** /**
...@@ -229,7 +235,9 @@ public class CacheLongKeyLIRS<V> { ...@@ -229,7 +235,9 @@ public class CacheLongKeyLIRS<V> {
*/ */
public V get(long key) { public V get(long key) {
int hash = getHash(key); int hash = getHash(key);
return getSegment(hash).get(key, hash); Segment<V> s = getSegment(hash);
Entry<V> e = s.find(key, hash);
return s.get(e);
} }
private Segment<V> getSegment(int hash) { private Segment<V> getSegment(int hash) {
...@@ -553,11 +561,17 @@ public class CacheLongKeyLIRS<V> { ...@@ -553,11 +561,17 @@ public class CacheLongKeyLIRS<V> {
private final int mask; private final int mask;
/** /**
* The number of entries in the non-resident queue, as a factor of the * Low watermark for the number of entries in the non-resident queue,
* number of entries in the map. * as a factor of the number of entries in the map.
*/ */
private final int nonResidentQueueSize; private final int nonResidentQueueSize;
/**
* High watermark for the number of entries in the non-resident queue,
* as a factor of the number of entries in the map.
*/
private final int nonResidentQueueSizeHigh;
/** /**
* The stack of recently referenced elements. This includes all hot * The stack of recently referenced elements. This includes all hot
* entries, and the recently referenced cold entries. Resident cold * entries, and the recently referenced cold entries. Resident cold
...@@ -594,18 +608,19 @@ public class CacheLongKeyLIRS<V> { ...@@ -594,18 +608,19 @@ public class CacheLongKeyLIRS<V> {
/** /**
* Create a new cache segment. * Create a new cache segment.
*
* @param maxMemory the maximum memory to use * @param maxMemory the maximum memory to use
* @param stackMoveDistance the number of other entries to be moved to * @param stackMoveDistance the number of other entries to be moved to
* the top of the stack before moving an entry to the top * the top of the stack before moving an entry to the top
* @param len the number of hash table buckets (must be a power of 2) * @param len the number of hash table buckets (must be a power of 2)
* @param nonResidentQueueSize the non-resident queue size factor * @param nonResidentQueueSize the non-resident queue size low watermark factor
* @param nonResidentQueueSizeHigh the non-resident queue size high watermark factor
*/ */
Segment(long maxMemory, int stackMoveDistance, int len, Segment(long maxMemory, int stackMoveDistance, int len,
int nonResidentQueueSize) { int nonResidentQueueSize, int nonResidentQueueSizeHigh) {
setMaxMemory(maxMemory); setMaxMemory(maxMemory);
this.stackMoveDistance = stackMoveDistance; this.stackMoveDistance = stackMoveDistance;
this.nonResidentQueueSize = nonResidentQueueSize; this.nonResidentQueueSize = nonResidentQueueSize;
this.nonResidentQueueSizeHigh = nonResidentQueueSizeHigh;
// the bit mask has all bits set // the bit mask has all bits set
mask = len - 1; mask = len - 1;
...@@ -632,12 +647,13 @@ public class CacheLongKeyLIRS<V> { ...@@ -632,12 +647,13 @@ public class CacheLongKeyLIRS<V> {
* @param len the number of hash table buckets (must be a power of 2) * @param len the number of hash table buckets (must be a power of 2)
*/ */
Segment(Segment<V> old, int len) { Segment(Segment<V> old, int len) {
this(old.maxMemory, old.stackMoveDistance, len, old.nonResidentQueueSize); this(old.maxMemory, old.stackMoveDistance, len,
old.nonResidentQueueSize, old.nonResidentQueueSizeHigh);
hits = old.hits; hits = old.hits;
misses = old.misses; misses = old.misses;
Entry<V> s = old.stack.stackPrev; Entry<V> s = old.stack.stackPrev;
while (s != old.stack) { while (s != old.stack) {
Entry<V> e = copy(s); Entry<V> e = new Entry<>(s);
addToMap(e); addToMap(e);
addToStack(e); addToStack(e);
s = s.stackPrev; s = s.stackPrev;
...@@ -646,7 +662,7 @@ public class CacheLongKeyLIRS<V> { ...@@ -646,7 +662,7 @@ public class CacheLongKeyLIRS<V> {
while (s != old.queue) { while (s != old.queue) {
Entry<V> e = find(s.key, getHash(s.key)); Entry<V> e = find(s.key, getHash(s.key));
if (e == null) { if (e == null) {
e = copy(s); e = new Entry<>(s);
addToMap(e); addToMap(e);
} }
addToQueue(queue, e); addToQueue(queue, e);
...@@ -656,7 +672,7 @@ public class CacheLongKeyLIRS<V> { ...@@ -656,7 +672,7 @@ public class CacheLongKeyLIRS<V> {
while (s != old.queue2) { while (s != old.queue2) {
Entry<V> e = find(s.key, getHash(s.key)); Entry<V> e = find(s.key, getHash(s.key));
if (e == null) { if (e == null) {
e = copy(s); e = new Entry<>(s);
addToMap(e); addToMap(e);
} }
addToQueue(queue2, e); addToQueue(queue2, e);
...@@ -690,54 +706,25 @@ public class CacheLongKeyLIRS<V> { ...@@ -690,54 +706,25 @@ public class CacheLongKeyLIRS<V> {
mapSize++; mapSize++;
} }
private static <V> Entry<V> copy(Entry<V> old) {
Entry<V> e = new Entry<>(old.memory);
e.key = old.key;
e.value = old.value;
e.reference = old.reference;
e.topMove = old.topMove;
return e;
}
/**
* Get the memory used for the given key.
*
* @param key the key (may not be null)
* @param hash the hash
* @return the memory, or 0 if there is no resident entry
*/
int getMemory(long key, int hash) {
Entry<V> e = find(key, hash);
return e == null ? 0 : e.getMemory();
}
/** /**
* Get the value for the given key if the entry is cached. This method * Get the value from the given entry.
* adjusts the internal state of the cache sometimes, to ensure commonly * This method adjusts the internal state of the cache sometimes,
* used entries stay in the cache. * to ensure commonly used entries stay in the cache.
* *
* @param key the key (may not be null) * @param e the entry
* @param hash the hash
* @return the value, or null if there is no resident entry * @return the value, or null if there is no resident entry
*/ */
V get(long key, int hash) { synchronized V get(Entry<V> e) {
Entry<V> e = find(key, hash); V value = e == null ? null : e.getValue();
synchronized (this) {
if (e == null) {
// the entry was not found
misses++;
return null;
}
V value = e.getValue();
if (value == null) { if (value == null) {
// it was a non-resident entry // the entry was not found
// or it was a non-resident entry
misses++; misses++;
return null; } else {
}
access(e); access(e);
hits++; hits++;
return value;
} }
return value;
} }
/** /**
...@@ -749,8 +736,7 @@ public class CacheLongKeyLIRS<V> { ...@@ -749,8 +736,7 @@ public class CacheLongKeyLIRS<V> {
private void access(Entry<V> e) { private void access(Entry<V> e) {
if (e.isHot()) { if (e.isHot()) {
if (e != stack.stackNext && e.stackNext != null) { if (e != stack.stackNext && e.stackNext != null) {
if (stackMoveDistance == 0 || if (stackMoveCounter - e.topMove > stackMoveDistance) {
stackMoveCounter - e.topMove > stackMoveDistance) {
// move a hot entry to the top of the stack // move a hot entry to the top of the stack
// unless it is already there // unless it is already there
boolean wasEnd = e == stack.stackPrev; boolean wasEnd = e == stack.stackPrev;
...@@ -806,10 +792,6 @@ public class CacheLongKeyLIRS<V> { ...@@ -806,10 +792,6 @@ public class CacheLongKeyLIRS<V> {
* @return the old value, or null if there was no resident entry * @return the old value, or null if there was no resident entry
*/ */
synchronized V put(long key, int hash, V value, int memory) { synchronized V put(long key, int hash, V value, int memory) {
if (value == null) {
throw DataUtils.newIllegalArgumentException(
"The value may not be null");
}
Entry<V> e = find(key, hash); Entry<V> e = find(key, hash);
boolean existed = e != null; boolean existed = e != null;
V old = null; V old = null;
...@@ -821,9 +803,7 @@ public class CacheLongKeyLIRS<V> { ...@@ -821,9 +803,7 @@ public class CacheLongKeyLIRS<V> {
// the new entry is too big to fit // the new entry is too big to fit
return old; return old;
} }
e = new Entry<>(memory); e = new Entry<>(key, value, memory);
e.key = key;
e.value = value;
int index = hash & mask; int index = hash & mask;
e.mapNext = entries[index]; e.mapNext = entries[index];
entries[index] = e; entries[index] = e;
...@@ -930,20 +910,17 @@ public class CacheLongKeyLIRS<V> { ...@@ -930,20 +910,17 @@ public class CacheLongKeyLIRS<V> {
} }
void trimNonResidentQueue() { void trimNonResidentQueue() {
Entry<V> e; int residentCount = mapSize - queue2Size;
int maxQueue2Size = 4 * nonResidentQueueSize * (mapSize - queue2Size); int maxQueue2SizeHigh = nonResidentQueueSizeHigh * residentCount;
while (queue2Size > maxQueue2Size) { int maxQueue2Size = nonResidentQueueSize * residentCount;
e = queue2.queuePrev;
int hash = getHash(e.key);
remove(e.key, hash);
}
maxQueue2Size >>= 2;
while (queue2Size > maxQueue2Size) { while (queue2Size > maxQueue2Size) {
e = queue2.queuePrev; Entry<V> e = queue2.queuePrev;
if (queue2Size <= maxQueue2SizeHigh) {
WeakReference<V> reference = e.reference; WeakReference<V> reference = e.reference;
if (reference != null && reference.get() != null) { if (reference != null && reference.get() != null) {
break; // stop trimming if entry holds a value break; // stop trimming if entry holds a value
} }
}
int hash = getHash(e.key); int hash = getHash(e.key);
remove(e.key, hash); remove(e.key, hash);
} }
...@@ -1075,19 +1052,6 @@ public class CacheLongKeyLIRS<V> { ...@@ -1075,19 +1052,6 @@ public class CacheLongKeyLIRS<V> {
return keys; return keys;
} }
/**
* Check whether there is a resident entry for the given key. This
* method does not adjust the internal state of the cache.
*
* @param key the key (may not be null)
* @param hash the hash
* @return true if there is a resident entry
*/
boolean containsKey(long key, int hash) {
Entry<V> e = find(key, hash);
return e != null && e.value != null;
}
/** /**
* Get the set of keys for resident entries. * Get the set of keys for resident entries.
* *
...@@ -1131,7 +1095,7 @@ public class CacheLongKeyLIRS<V> { ...@@ -1131,7 +1095,7 @@ public class CacheLongKeyLIRS<V> {
/** /**
* The key. * The key.
*/ */
long key; final long key;
/** /**
* The value. Set to null for non-resident-cold entries. * The value. Set to null for non-resident-cold entries.
...@@ -1180,12 +1144,20 @@ public class CacheLongKeyLIRS<V> { ...@@ -1180,12 +1144,20 @@ public class CacheLongKeyLIRS<V> {
Entry<V> mapNext; Entry<V> mapNext;
public Entry() { Entry() {
this(0); this(0L, null, 0);
} }
public Entry(int memory) { Entry(long key, V value, int memory) {
this.key = key;
this.memory = memory; this.memory = memory;
this.value = value;
}
Entry(Entry<V> old) {
this(old.key, old.value, old.memory);
this.reference = old.reference;
this.topMove = old.topMove;
} }
/** /**
...@@ -1228,11 +1200,15 @@ public class CacheLongKeyLIRS<V> { ...@@ -1228,11 +1200,15 @@ public class CacheLongKeyLIRS<V> {
public int stackMoveDistance = 32; public int stackMoveDistance = 32;
/** /**
* The number of entries in the non-resident queue, as a factor of the * Low water mark for the number of entries in the non-resident queue,
* number of all other entries in the map. * as a factor of the number of all other entries in the map.
*/ */
public final int nonResidentQueueSize = 3; public final int nonResidentQueueSize = 3;
/**
* High watermark for the number of entries in the non-resident queue,
* as a factor of the number of all other entries in the map
*/
public final int nonResidentQueueSizeHigh = 12;
} }
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论