package org.cache2k.benchmark.impl2015; /* * #%L * Benchmarks: implementation variants * %% * Copyright (C) 2013 - 2017 headissue GmbH, Munich * %% * 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. * #L% */ /** * Implementation of the CAR replacement algorithm. This implementation is for evaluation only and * not yet tuned for production. * * @author Jens Wilke */ public class CarCache<K, T> extends LockFreeCache<CarCache.Entry, K, T> { int size; int arcP = 0; Hash<Entry> b1HashCtrl; Entry[] b1Hash; Hash<Entry> b2HashCtrl; Entry[] b2Hash; int t1Size; int t2Size; Entry<K,T> t2Head; Entry<K,T> t1Head; Entry<K,T> b1Head; Entry<K,T> b2Head; boolean b2HitPreferenceForEviction; long hits; protected void initializeHeapCache() { super.initializeHeapCache(); t1Size = 0; t2Size = 0; t1Head = null; t2Head = null; b1Head = new Entry<K, T>(); b2Head = new Entry<K, T>(); b1Head = new Entry<K,T>().shortCircuit(); b2Head = new Entry<K,T>().shortCircuit(); b1HashCtrl = new Hash<Entry>(); b2HashCtrl = new Hash<Entry>(); b1Hash = b1HashCtrl.init(Entry.class); b2Hash = b2HashCtrl.init(Entry.class); } @Override protected void recordHit(Entry e) { e.hitCnt++; } @Override protected void insertIntoReplacementList(Entry e) { size++; t1Size++; t1Head = insertIntoTailCyclicList(t1Head, e); } @Override protected Entry newEntry() { return new Entry(); } @Override public long getHitCnt() { return hits + sumUpListHits(t1Head) + sumUpListHits(t2Head); } private int sumUpListHits(Entry e) { if (e == null) { return 0; } int cnt = 0; Entry _head = e; do { cnt += e.hitCnt; e = (Entry) e.prev; } while (e != _head); return cnt; } @Override protected Entry checkForGhost(K key, int hc) { Entry e = b1HashCtrl.remove(b1Hash, key, hc); if (e != null) { removeFromList(e); b1HitAdaption(); insertT2(e); return e; } e = b2HashCtrl.remove(b2Hash, key, hc); if (e != null) { removeFromList(e); b2HitAdaption(); insertT2(e); return e; } return null; } private void b1HitAdaption() { int _b1Size = b1HashCtrl.size + 1; int _b2Size = b2HashCtrl.size; int _delta = _b1Size >= _b2Size ? 1 : _b2Size / _b1Size; arcP = Math.min(arcP + _delta, maxSize); b2HitPreferenceForEviction = false; } private void b2HitAdaption() { int _b1Size = b1HashCtrl.size; int _b2Size = b2HashCtrl.size + 1; int _delta = _b2Size >= _b1Size ? 1 : _b1Size / _b2Size; arcP = Math.max(arcP - _delta, 0); b2HitPreferenceForEviction = true; } @Override protected Entry findEvictionCandidate() { return replace(); } private Entry<K, T> replace() { Entry<K, T> e = null; for (;;) { if (t1Size >= Math.max(1, arcP) || t2Size == 0) { e = t1Head; if (e.hitCnt == 0) { t1Head = e.next; break; } hits += e.hitCnt; e.hitCnt = 0; t1Head = removeFromCyclicList(t1Head, e); t1Size--; t2Head = insertIntoTailCyclicList(t2Head, e); t2Size++; } else { e = t2Head; if (e.hitCnt == 0) { t2Head = e.next; break; } hits += e.hitCnt; e.hitCnt = 0; t2Head = e.next; } } evictGhosts(); return e; } private void evictGhosts() { int _b1Size = b1HashCtrl.size; if (t1Size + _b1Size == maxSize && _b1Size > 0) { Entry e = b1Head.prev; removeFromList(e); boolean f = b1HashCtrl.remove(b1Hash, e); return; } int _b2Size = b2HashCtrl.size; if (t1Size + t2Size + _b1Size + _b2Size > 2 * maxSize && _b2Size > 0) { Entry e = b2Head.prev; removeFromList(e); boolean f = b2HashCtrl.remove(b2Hash, e); } } private void insertT2(Entry<K, T> e) { t2Size++; t2Head = insertIntoTailCyclicList(t2Head, e); } private int getListSize() { return t1Size + t2Size; } private void insertCopyIntoB1(Entry<K, T> e) { Entry<K,T> e2 = copyEntryForGhost(e); b1Hash = b1HashCtrl.insert(b1Hash, e2); insertInList(b1Head, e2); } private void insertCopyIntoB2(Entry<K, T> e) { Entry<K,T> e2 = copyEntryForGhost(e); b2Hash = b2HashCtrl.insert(b2Hash, e2); insertInList(b2Head, e2); } private Entry<K, T> copyEntryForGhost(Entry<K, T> e) { Entry<K, T> e2; e2 = new Entry<K, T>(); e2.key = (K) e.key; e2.hashCode = e.hashCode; return e2; } @Override protected void removeEntryFromReplacementList(Entry e) { boolean _t1Hit = t1Head != null && t1Head.prev == e; boolean _t2Hit = t2Head != null && t2Head.prev == e; if (!_t1Hit || !_t2Hit) { if (t1Size < t2Size) { _t1Hit = false; _t2Hit = true; Entry<K, T> x = t1Head; if (x != null) { do { if (x == e) { _t1Hit = true; _t2Hit = false; break; } x = x.next; } while (x != t1Head); } } else { _t1Hit = true; _t2Hit = false; Entry<K, T> x = t2Head; if (x != null) { do { if (x == e) { _t1Hit = false; _t2Hit = true; break; } x = x.next; } while (x != t2Head); } } } if (_t1Hit) { insertCopyIntoB1(e); t1Head = removeFromCyclicList(t1Head, e); t1Size--; } else { insertCopyIntoB2(e); t2Head = removeFromCyclicList(t2Head, e); t2Size--; } } static class Entry<K, T> extends org.cache2k.benchmark.impl2015.Entry<Entry, K, T> { int hitCnt; } }