/*
* 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;
/**
* An implementation of TinySet's indexing method. A method to index a succinct hash table that is
* only 2 bits from theoretical lower bound. This is only the indexing technique and it helps
* calculate offsets in array using two indexes. chainIndex - (set bit for non empty chain/unset for
* empty) isLastIndex (set bit for last in chain/empty bit for not last in chain). Both indexes are
* assumed to be 64 bits, (longs) for efficiency and simplicity. The technique update the indexes
* upon addition/removal.
*
* Paper link:
* http://www.cs.technion.ac.il/users/wwwb/cgi-bin/tr-get.cgi/2015/CS/CS-2015-03.pdf
* Presentation:
* http://www.cs.technion.ac.il/~gilga/UCLA_and_TCL.pptx
*
* @author gilga1983@gmail.com (Gil Einziger)
*/
public final class TinySetIndexing {
// for performance - for functions that need to know both the start and the end of the chain.
static int chainStart;
static int chainEnd;
private TinySetIndexing() {}
public static int getChainStart(HashedItem fpaux, long[] chainIndex, long[] isLastIndex) {
int requiredChainNumber = rank(chainIndex[fpaux.set], fpaux.chainId);
int currentChainNumber = rank(isLastIndex[fpaux.set], requiredChainNumber);
int currentOffset = requiredChainNumber;
long tempisLastIndex = isLastIndex[fpaux.set] >>> requiredChainNumber;
while (currentChainNumber < requiredChainNumber) {
currentChainNumber += tempisLastIndex & 1L;
currentOffset++;
tempisLastIndex >>>= 1;
}
return currentOffset;
}
public static int rank(long index, int bitNum) {
return Long.bitCount(index & (~(-1L << bitNum)));
}
public static int getChain(HashedItem fpaux, long[] chainIndex, long[] isLastIndex) {
int requiredChainNumber = rank(chainIndex[fpaux.set], fpaux.chainId);
int currentChainNumber = rank(isLastIndex[fpaux.set], requiredChainNumber);
int currentOffset = requiredChainNumber;
long tempisLastIndex = isLastIndex[fpaux.set] >>> requiredChainNumber;
while (currentChainNumber < requiredChainNumber) {
currentChainNumber += tempisLastIndex & 1L;
currentOffset++;
tempisLastIndex >>>= 1;
}
TinySetIndexing.chainStart = currentOffset;
while ((tempisLastIndex & 1L) == 0) {
currentOffset++;
tempisLastIndex >>>= 1;
}
TinySetIndexing.chainEnd = currentOffset;
return currentOffset;
}
public static int getChainAtOffset(HashedItem fpaux, long[] chainIndex, long[] isLastIndex,
int offset) {
int nonEmptyChainsToSee = rank(isLastIndex[fpaux.set], offset);
int nonEmptyChainSeen = rank(chainIndex[fpaux.set], nonEmptyChainsToSee);
for (int i = nonEmptyChainsToSee; i <= 64;) {
if (TinySetIndexing.chainExist(chainIndex[fpaux.set], i)
&& (nonEmptyChainSeen == nonEmptyChainsToSee)) {
return i;
}
i += Math.max(1, nonEmptyChainsToSee - nonEmptyChainSeen);
nonEmptyChainSeen = rank(chainIndex[fpaux.set], i);
}
throw new RuntimeException("Cannot choose victim!");
}
public static boolean chainExist(long chainIndex, int chainId) {
return (chainIndex | (1L << chainId)) == chainIndex;
}
public static int addItem(HashedItem fpaux, long[] chainIndex, long[] isLastIndex) {
int offset = getChainStart(fpaux, chainIndex, isLastIndex);
long mask = 1L << fpaux.chainId;
isLastIndex[fpaux.set] = extendZero(isLastIndex[fpaux.set], offset);
// if the item is new...
if ((mask | chainIndex[fpaux.set]) != chainIndex[fpaux.set]) {
// add new chain to IO.
chainIndex[fpaux.set] |= mask;
// mark item as last in isLastIndex.
isLastIndex[fpaux.set] |= (1L << offset);
}
return offset;
}
private static long extendZero(final long isLastIndex, final int offset) {
long constantPartMask = (1L << offset) - 1;
return (isLastIndex & constantPartMask)
| ((isLastIndex << 1L)
& (~(constantPartMask))
& (~(1L << offset)));
}
private static long shrinkOffset(long isLastIndex, int offset) {
long conMask = ((1L << offset) - 1);
return (isLastIndex & conMask) | (((~conMask) & isLastIndex) >>> 1);
}
public static void removeItem(HashedItem fpaux, long[] chainIndex, long[] isLastIndex) {
int chainStart = getChainStart(fpaux, chainIndex, isLastIndex);
// avoid an if command: either update chainIndex to the new state or keep it the way it is.
chainIndex[fpaux.set] = (isLastIndex[fpaux.set] & (1L << chainStart)) == 0L
? chainIndex[fpaux.set]
: chainIndex[fpaux.set] & ~(1L << fpaux.chainId);
// update isLastIndex.
isLastIndex[fpaux.set] = shrinkOffset(isLastIndex[fpaux.set], chainStart);
}
}