
be.bagofwords.cache.Cache Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bow-utils Show documentation
Show all versions of bow-utils Show documentation
Utility classes that are used in the count-db project and other bow-* projects
package be.bagofwords.cache;
import be.bagofwords.counts.Counter;
import be.bagofwords.util.KeyValue;
import it.unimi.dsi.fastutil.longs.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Semaphore;
public class Cache {
private static final int NUMBER_OF_SEGMENTS_EXPONENT = 8;
private static final int NUMBER_OF_SEGMENTS = 1 << NUMBER_OF_SEGMENTS_EXPONENT;
private static final long SEGMENTS_KEY_MASK = NUMBER_OF_SEGMENTS - 1;
private static final int NUMBER_OF_READ_PERMITS = 1000;
private final Semaphore[] locks;
private final Map[] cachedObjects;
private final Map[] oldCachedObjects;
private final boolean isWriteBuffer;
private final Class extends T> objectClass;
private final T nullValue;
private final String name;
private long numHits;
private long numOfFetches;
private Map commonValues;
public Cache(boolean isWriteBuffer, String name, Class extends T> objectClass) {
this.objectClass = objectClass;
this.cachedObjects = new Map[NUMBER_OF_SEGMENTS];
createMaps(cachedObjects);
this.oldCachedObjects = new Map[NUMBER_OF_SEGMENTS];
createMaps(oldCachedObjects);
this.locks = new Semaphore[NUMBER_OF_SEGMENTS];
for (int i = 0; i < locks.length; i++) {
this.locks[i] = new Semaphore(NUMBER_OF_READ_PERMITS);
}
this.numHits = 0;
this.numOfFetches = 0;
this.commonValues = null; //Will be initialized once we have enough values
this.isWriteBuffer = isWriteBuffer;
this.nullValue = getNullValueForType(objectClass);
this.name = name;
}
public T get(long key) {
incrementFetches();
int segmentInd = getSegmentInd(key);
lockRead(segmentInd);
T result = cachedObjects[segmentInd].get(key);
if (result == null) {
//maybe in old objects?
result = oldCachedObjects[segmentInd].get(key);
unlockRead(segmentInd);
if (result != null) {
lockWrite(segmentInd);
//found in old, put in new
cachedObjects[segmentInd].put(key, result);
oldCachedObjects[segmentInd].remove(key);
unlockWrite(segmentInd);
} else {
return null;
}
} else {
unlockRead(segmentInd);
}
incrementHits();
if (result.equals(nullValue)) {
return null;
} else {
return result;
}
}
public void put(long key, T value) {
if (value == null) {
value = nullValue;
} else if (value.equals(nullValue)) {
throw new RuntimeException("Sorry but " + value + " is a reserved value to indicate null.");
} else {
value = makeSharedValueIfPossible(value);
}
int segmentInd = getSegmentInd(key);
lockWrite(segmentInd);
cachedObjects[segmentInd].put(key, value);
oldCachedObjects[segmentInd].remove(key);
unlockWrite(segmentInd);
}
public List> removeAllValues() {
final List> valuesToRemove = new ArrayList<>();
doActionOnValues(new ValueAction() {
@Override
public void doAction(long key, Object value) {
valuesToRemove.add(new KeyValue(key, value));
}
});
clear(); //also clear old cached objects
return valuesToRemove;
}
public void clear() {
lockWriteAll();
createMaps(cachedObjects);
createMaps(oldCachedObjects);
unlockWriteAll();
}
public void moveCachedObjectsToOld() {
lockWriteAll();
for (int i = 0; i < cachedObjects.length; i++) {
oldCachedObjects[i] = cachedObjects[i];
}
createMaps(cachedObjects);
unlockWriteAll();
}
public long size() {
long result = 0;
for (Map map : cachedObjects) {
result += map.size();
}
return result;
}
public long completeSize() {
long result = size();
for (Map map : oldCachedObjects) {
result += map.size();
}
return result;
}
public void remove(long key) {
int segmentInd = getSegmentInd(key);
lockWrite(segmentInd);
cachedObjects[segmentInd].remove(key);
oldCachedObjects[segmentInd].remove(key);
unlockWrite(segmentInd);
}
public String getName() {
return name;
}
public long getNumberOfHits() {
return numHits;
}
public long getNumberOfFetches() {
return numOfFetches;
}
public boolean isWriteBuffer() {
return isWriteBuffer;
}
private static T getNullValueForType(Class objectClass) {
if (objectClass == Long.class) {
return (T) new Long(Long.MAX_VALUE);
} else if (objectClass == Double.class) {
return (T) new Double(Double.MAX_VALUE);
} else if (objectClass == Float.class) {
return (T) new Float(Float.MAX_VALUE);
} else if (objectClass == Integer.class) {
return (T) new Integer(Integer.MAX_VALUE);
} else {
return (T) "xxxNULLxxx"; //we don't use a primitive map, so we can just use any object here
}
}
private void lockRead(int segmentInd) {
locks[segmentInd].acquireUninterruptibly(1);
}
private void unlockRead(int segmentInd) {
locks[segmentInd].release(1);
}
private void lockWrite(int segmentInd) {
locks[segmentInd].acquireUninterruptibly(NUMBER_OF_READ_PERMITS);
}
private void unlockWrite(int segmentInd) {
locks[segmentInd].release(NUMBER_OF_READ_PERMITS);
}
private void lockWriteAll() {
for (int i = 0; i < NUMBER_OF_SEGMENTS; i++) {
locks[i].acquireUninterruptibly(NUMBER_OF_READ_PERMITS);
}
}
private void unlockWriteAll() {
for (int i = 0; i < NUMBER_OF_SEGMENTS; i++) {
locks[i].release(NUMBER_OF_READ_PERMITS);
}
}
private void doActionOnValues(ValueAction valueAction) {
for (int segmentInd = 0; segmentInd < NUMBER_OF_SEGMENTS; segmentInd++) {
lockRead(segmentInd);
for (Map.Entry entry : cachedObjects[segmentInd].entrySet()) {
T value = entry.getValue();
if (value.equals(nullValue)) {
value = null;
}
valueAction.doAction(entry.getKey(), value);
}
unlockRead(segmentInd);
}
}
private void incrementFetches() {
this.numOfFetches++;
}
private void incrementHits() {
this.numHits++;
}
private void createMaps(Map[] result) {
for (int i = 0; i < result.length; i++) {
if (objectClass == Long.class) {
result[i] = new Long2LongOpenHashMap();
} else if (objectClass == Integer.class) {
result[i] = new Long2IntOpenHashMap();
} else if (objectClass == Float.class) {
result[i] = new Long2FloatOpenHashMap();
} else if (objectClass == Double.class) {
result[i] = new Long2DoubleOpenHashMap();
} else {
result[i] = new Long2ObjectOpenHashMap();
}
}
}
private int getSegmentInd(long key) {
return (int) (key & SEGMENTS_KEY_MASK);
}
private T makeSharedValueIfPossible(T value) {
if (valueCanBeCommon(value)) {
if (commonValues == null) {
//Can we compute the common values?
if (size() > 10000) {
commonValues = computeCommonValues();
}
}
if (commonValues != null) {
//Fetch common value
T commonValue = commonValues.get(value);
if (commonValue != null) {
return commonValue;
}
}
}
return value;
}
private boolean valueCanBeCommon(T value) {
return value != null && (value instanceof String || value instanceof Byte || value instanceof Character || value instanceof Boolean);
}
private Map computeCommonValues() {
final Counter
© 2015 - 2025 Weber Informatics LLC | Privacy Policy