/* * 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 model that takes advantage of random eviction policy with * a ghost cache as admission policy. It offers a very dense memory layout combined with * (relative) speed at the expense of limited associativity. s * * @author gilga1983@gmail.com (Gil Einziger) */ @SuppressWarnings("PMD.AvoidDollarSigns") public final class TinyCache { public final long[] chainIndex; public final long[] isLastIndex; private final HashFunctionParser hashFunc; private final int itemsPerSet; private final long[] cache; private final Random rnd; public TinyCache(int nrSets, int itemsPerSet, int randomSeed) { chainIndex = new long[nrSets]; isLastIndex = new long[nrSets]; hashFunc = new HashFunctionParser(nrSets); this.itemsPerSet = itemsPerSet; cache = new long[nrSets * itemsPerSet]; rnd = new Random(randomSeed); } public boolean contains(long item) { hashFunc.createHash(item); if (!TinySetIndexing.chainExist(chainIndex[hashFunc.fpaux.set], hashFunc.fpaux.chainId)) { return false; } 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 { if (cache[TinySetIndexing.chainStart % cache.length] == hashFunc.fpaux.value) { return true; } TinySetIndexing.chainStart++; } catch (Exception e) { System.out.println(" length: " + cache.length + " Access: " + TinySetIndexing.chainStart); } } return false; } /** * 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.value, bucketStart, delta); return removedOffset; } public boolean addItem(long item) { hashFunc.createHash(item); int bucketStart = this.itemsPerSet * hashFunc.fpaux.set; if (cache[bucketStart + this.itemsPerSet - 1] != 0) { return selectVictim(bucketStart); } int idxToAdd = TinySetIndexing.addItem(hashFunc.fpaux, chainIndex, isLastIndex); this.replaceItems(idxToAdd, hashFunc.fpaux.value, bucketStart, 1); return false; } private boolean selectVictim(int bucketStart) { byte victimOffset = (byte) rnd.nextInt(this.itemsPerSet); int victimChain = TinySetIndexing.getChainAtOffset(hashFunc.fpaux, chainIndex, isLastIndex, victimOffset); // this if is still for debugging and common sense. Should be eliminated for performance once // I am sure of the correctness. if (TinySetIndexing.chainExist(chainIndex[hashFunc.fpaux.set], victimChain)) { replace(hashFunc.fpaux, (byte) victimChain, bucketStart, victimOffset); return true; } else { throw new RuntimeException("Failed to replace"); } } private void replaceItems(final int idx, long value, int start, final int delta) { start += idx; long $; do { $ = this.cache[start]; this.cache[start] = value; value = $; start += delta; } while (value != 0); } }