/*
* Copyright 2015 Ben Manes. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.benmanes.caffeine.cache.simulator.admission.countmin4;
import javax.annotation.Nonnegative;
import com.github.benmanes.caffeine.cache.simulator.BasicSettings;
import com.github.benmanes.caffeine.cache.simulator.BasicSettings.TinyLfuSettings.DoorkeeperSettings;
import com.github.benmanes.caffeine.cache.simulator.membership.FilterType;
import com.github.benmanes.caffeine.cache.simulator.membership.Membership;
import com.typesafe.config.Config;
/**
* A sketch where the aging process is a periodic reset.
*
* @author ben.manes@gmail.com (Ben Manes)
*/
public final class PeriodicResetCountMin4 extends CountMin4 {
static final long ONE_MASK = 0x1111111111111111L;
final Membership doorkeeper;
int additions;
int period;
public PeriodicResetCountMin4(Config config) {
super(config);
BasicSettings settings = new BasicSettings(config);
DoorkeeperSettings doorkeeperSettings = settings.tinyLfu().countMin4().periodic().doorkeeper();
if (doorkeeperSettings.enabled()) {
FilterType filterType = settings.membershipFilter();
double expectedInsertionsMultiplier = doorkeeperSettings.expectedInsertionsMultiplier();
long expectedInsertions = (long) expectedInsertionsMultiplier * settings.maximumSize();
doorkeeper = filterType.create(expectedInsertions, doorkeeperSettings.fpp(), config);
} else {
doorkeeper = Membership.disabled();
}
}
@Override
protected void ensureCapacity(@Nonnegative long maximumSize) {
super.ensureCapacity(maximumSize);
period = (maximumSize == 0) ? 10 : (10 * table.length);
if (period <= 0) {
period = Integer.MAX_VALUE;
}
}
@Override
public int frequency(long e) {
int count = super.frequency(e);
if (doorkeeper.mightContain(e)) {
count++;
}
return count;
}
@Override
public void increment(long e) {
if (!doorkeeper.put(e)) {
super.increment(e);
}
}
/**
* Reduces every counter by half of its original value. To reduce the truncation error, the sample
* is reduced by the number of counters with an odd value.
*/
@Override
protected void tryReset(boolean added) {
if (!added) {
return;
}
additions++;
if (additions != period) {
return;
}
int count = 0;
for (int i = 0; i < table.length; i++) {
count += Long.bitCount(table[i] & ONE_MASK);
table[i] = (table[i] >>> 1) & RESET_MASK;
}
additions = (additions >>> 1) - (count >>> 2);
doorkeeper.clear();
}
}