/*
* Copyright 2015 Gilga Einziger. 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.tinycache;
import java.util.Random;
/**
* This is the TinyCache sketch that is based on TinySet and TinyTable. It is adopted for fast
* operation and bounded memory footprint. When a set is full, a victim is selected at random from
* the full set. (basically some sort of random removal cache).
*
* @author gilga1983@gmail.com (Gil Einziger)
*/
@SuppressWarnings("PMD.AvoidDollarSigns")
public final class TinyCacheSketch {
public final long[] chainIndex;
public final long[] isLastIndex;
private final HashFunctionParser hashFunc;
private final int itemsPerSet;
private final byte[] cache;
private final Random rnd;
public TinyCacheSketch(int nrSets, int itemsPerSet, int randomSeed) {
chainIndex = new long[nrSets];
isLastIndex = new long[nrSets];
hashFunc = new HashFunctionParser(nrSets);
this.itemsPerSet = itemsPerSet;
cache = new byte[nrSets * itemsPerSet];
rnd = new Random(randomSeed);
}
public int countItem(long item) {
hashFunc.createHash(item);
int $ = 0;
if (!TinySetIndexing.chainExist(chainIndex[hashFunc.fpaux.set], hashFunc.fpaux.chainId)) {
return 0;
}
TinySetIndexing.getChain(hashFunc.fpaux, chainIndex, isLastIndex);
int offset = this.itemsPerSet * hashFunc.fpaux.set;
TinySetIndexing.chainStart += offset;
TinySetIndexing.chainEnd += offset;
// Gil : I think some of these tests are, I till carefully examine this function when I have
// time. As far as I understand it is working right now.
while (TinySetIndexing.chainStart <= TinySetIndexing.chainEnd) {
try {
$ += (cache[TinySetIndexing.chainStart % cache.length] == hashFunc.fpaux.fingerprint) ? 1L
: 0L;
TinySetIndexing.chainStart++;
} catch (Exception e) {
System.out.println(" length: " + cache.length + " Access: " + TinySetIndexing.chainStart);
// e.printStackTrace();
}
}
return $;
}
/**
* Implementing add and remove together in one function, means that less items are shifted.
* (reduction of 3 times from trivial implementation).
*/
private int replace(HashedItem fpaux, byte victim, int bucketStart, int removedOffset) {
byte chainId = fpaux.chainId;
fpaux.chainId = victim;
this.cache[bucketStart + removedOffset] = 0;
TinySetIndexing.removeItem(fpaux, chainIndex, isLastIndex);
fpaux.chainId = chainId;
int idxToAdd = TinySetIndexing.addItem(fpaux, chainIndex, isLastIndex);
int delta = (removedOffset < idxToAdd) ? -1 : 1;
replaceItems(idxToAdd, fpaux.fingerprint, bucketStart, delta);
return removedOffset;
}
public void addItem(long item) {
hashFunc.createHash(item);
int bucketStart = this.itemsPerSet * hashFunc.fpaux.set;
if (cache[bucketStart + this.itemsPerSet - 1] != 0) {
selectVictim(bucketStart);
return;
}
int idxToAdd = TinySetIndexing.addItem(hashFunc.fpaux, chainIndex, isLastIndex);
this.replaceItems(idxToAdd, hashFunc.fpaux.fingerprint, bucketStart, 1);
}
private void selectVictim(int bucketStart) {
byte victimOffset = (byte) rnd.nextInt(this.itemsPerSet);
int victimChain =
TinySetIndexing.getChainAtOffset(hashFunc.fpaux, chainIndex, isLastIndex, victimOffset);
if (TinySetIndexing.chainExist(chainIndex[hashFunc.fpaux.set], victimChain)) {
replace(hashFunc.fpaux, (byte) victimChain, bucketStart, victimOffset);
} else {
throw new RuntimeException("Failed to replace");
}
}
private void replaceItems(final int idx, byte value, int start, final int delta) {
start += idx;
byte $;
do {
$ = this.cache[start];
this.cache[start] = value;
value = $;
start += delta;
} while (value != 0);
}
}