Skip to content

Commit

Permalink
Add tests for adaptive eviction (fixes #106, #263, #289)
Browse files Browse the repository at this point in the history
  • Loading branch information
ben-manes committed Feb 9, 2019
1 parent 2e1db0c commit faf7370
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ abstract class BoundedLocalCache<K, V> extends BLCHeader.DrainStatusRef<K, V>
static final int WRITE_BUFFER_RETRIES = 100;
/** The maximum weighted capacity of the map. */
static final long MAXIMUM_CAPACITY = Long.MAX_VALUE - Integer.MAX_VALUE;
/** The percent of the maximum weighted capacity dedicated to the main space. */
/** The initial percent of the maximum weighted capacity dedicated to the main space. */
static final double PERCENT_MAIN = 0.99d;
/** The percent of the maximum weighted capacity dedicated to the main's protected space. */
static final double PERCENT_MAIN_PROTECTED = 0.80d;
Expand Down Expand Up @@ -576,6 +576,9 @@ protected void setAdjustment(long amount) {
@GuardedBy("evictionLock")
void setMaximumSize(long maximum) {
requireArgument(maximum >= 0);
if (maximum == maximum()) {
return;
}

long max = Math.min(maximum, MAXIMUM_CAPACITY);
long window = max - (long) (PERCENT_MAIN * max);
Expand Down Expand Up @@ -1017,9 +1020,9 @@ void increaseWindow() {

quota -= weight;
if (probation) {
setMainProtectedWeightedSize(mainProtectedWeightedSize() - weight);
accessOrderProbationDeque().remove(candidate);
} else {
setMainProtectedWeightedSize(mainProtectedWeightedSize() - weight);
accessOrderProtectedDeque().remove(candidate);
}
setWindowWeightedSize(windowWeightedSize() + weight);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ final class FrequencySketch<E> {
* inexpensive bit manipulations per array location.
*
* [1] An Improved Data Stream Summary: The Count-Min Sketch and its Applications
* https://dimacs.rutgers.edu/~graham/pubs/papers/cm-full.pdf
* http://dimacs.rutgers.edu/~graham/pubs/papers/cm-full.pdf
* [2] TinyLFU: A Highly Efficient Cache Admission Policy
* https://dl.acm.org/citation.cfm?id=3149371
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,18 @@
import static com.github.benmanes.caffeine.cache.BLCHeader.DrainStatusRef.PROCESSING_TO_IDLE;
import static com.github.benmanes.caffeine.cache.BLCHeader.DrainStatusRef.PROCESSING_TO_REQUIRED;
import static com.github.benmanes.caffeine.cache.BLCHeader.DrainStatusRef.REQUIRED;
import static com.github.benmanes.caffeine.cache.BoundedLocalCache.PERCENT_MAIN_PROTECTED;
import static com.github.benmanes.caffeine.cache.testing.HasRemovalNotifications.hasRemovalNotifications;
import static com.github.benmanes.caffeine.cache.testing.HasStats.hasEvictionCount;
import static com.github.benmanes.caffeine.testing.Awaits.await;
import static java.util.Arrays.asList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.either;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.lessThan;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.mockito.ArgumentMatchers.any;
Expand Down Expand Up @@ -601,4 +605,79 @@ void checkDrainBlocks(BoundedLocalCache<Integer, Integer> localCache, Runnable t
}
await().untilTrue(done);
}

@Test(dataProvider = "caches")
@CacheSpec(compute = Compute.SYNC, implementation = Implementation.Caffeine,
population = Population.FULL, maximumSize = Maximum.FULL,
weigher = {CacheWeigher.DEFAULT, CacheWeigher.TEN})
public void adapt_increaseWindow(Cache<Integer, Integer> cache, CacheContext context) {
BoundedLocalCache<Integer, Integer> localCache = prepareForAdaption(
cache, context, /* make frequency-bias */ false);

int sampleSize = localCache.frequencySketch().sampleSize;
long protectedSize = localCache.mainProtectedWeightedSize();
long protectedMaximum = localCache.mainProtectedMaximum();
long windowSize = localCache.windowWeightedSize();
long windowMaximum = localCache.windowMaximum();

localCache.setPreviousSampleHitRate(0.80);
localCache.setMissesInSample(sampleSize / 2);
localCache.setHitsInSample(sampleSize - localCache.missesInSample());
localCache.climb();

// Fill main protected space
cache.asMap().keySet().stream().forEach(cache::getIfPresent);

assertThat(localCache.mainProtectedMaximum(),
is(either(lessThan(protectedMaximum)).or(is(0L))));
assertThat(localCache.mainProtectedWeightedSize(),
is(either(lessThan(protectedSize)).or(is(0L))));
assertThat(localCache.windowMaximum(), is(greaterThan(windowMaximum)));
assertThat(localCache.windowWeightedSize(), is(greaterThan(windowSize)));
}

@Test(dataProvider = "caches")
@CacheSpec(compute = Compute.SYNC, implementation = Implementation.Caffeine,
population = Population.FULL, maximumSize = Maximum.FULL,
weigher = {CacheWeigher.DEFAULT, CacheWeigher.TEN})
public void adapt_decreaseWindow(Cache<Integer, Integer> cache, CacheContext context) {
BoundedLocalCache<Integer, Integer> localCache = prepareForAdaption(
cache, context, /* make recency-bias */ true);

int sampleSize = localCache.frequencySketch().sampleSize;
long protectedSize = localCache.mainProtectedWeightedSize();
long protectedMaximum = localCache.mainProtectedMaximum();
long windowSize = localCache.windowWeightedSize();
long windowMaximum = localCache.windowMaximum();

localCache.setPreviousSampleHitRate(0.80);
localCache.setMissesInSample(sampleSize / 2);
localCache.setHitsInSample(sampleSize - localCache.missesInSample());
localCache.climb();

// Fill main protected space
cache.asMap().keySet().stream().forEach(cache::getIfPresent);

assertThat(localCache.mainProtectedMaximum(), is(greaterThan(protectedMaximum)));
assertThat(localCache.mainProtectedWeightedSize(), is(greaterThan(protectedSize)));
assertThat(localCache.windowMaximum(), is(either(lessThan(windowMaximum)).or(is(0L))));
assertThat(localCache.windowWeightedSize(), is(either(lessThan(windowSize)).or(is(0L))));
}

private BoundedLocalCache<Integer, Integer> prepareForAdaption(
Cache<Integer, Integer> cache, CacheContext context, boolean recencyBias) {
BoundedLocalCache<Integer, Integer> localCache = asBoundedLocalCache(cache);

localCache.setStepSize((recencyBias ? 1 : -1) * Math.abs(localCache.stepSize()));
localCache.setWindowMaximum((long) (0.5 * context.maximumWeightOrSize()));
localCache.setMainProtectedMaximum((long)
(PERCENT_MAIN_PROTECTED * (context.maximumWeightOrSize() - localCache.windowMaximum())));

// Fill window and main spaces
cache.invalidateAll();
cache.asMap().putAll(context.original());
cache.asMap().keySet().stream().forEach(cache::getIfPresent);
cache.asMap().keySet().stream().forEach(cache::getIfPresent);
return localCache;
}
}

0 comments on commit faf7370

Please sign in to comment.