Cleaned up MultiMaps

This commit is contained in:
leventov
2014-11-11 12:10:30 +03:00
parent 40b1f7cf77
commit 7e6c361d4e
5 changed files with 98 additions and 134 deletions
@@ -30,38 +30,40 @@ import static net.openhft.lang.Maths.isPowerOf2;
*/
class Int24Int24MultiMap implements MultiMap {
static final int MAX_CAPACITY = (1 << 24);
static final long MAX_CAPACITY = (1L << 24);
private static final int ENTRY_SIZE = 6;
// private static final int ENTRY_SIZE_SHIFT = 3;
private static final long ENTRY_SIZE = 6L;
private static final int UNSET_KEY = 0;
private static final int MASK = 0xFFFFFF;
private static final int HASH_INSTEAD_OF_UNSET_KEY = MASK;
private static final long MASK = 0xFFFFFFL;
private static final long HASH_INSTEAD_OF_UNSET_KEY = MASK;
private static final long UNSET_ENTRY = 0L;
final DirectBitSet positions;
private final int capacity;
private final int capacityMask;
private final int capacityMod2;
private final long capacityMask;
private final long capacityInBytes;
private final Bytes bytes;
private int searchHash = -1;
private int searchPos = -1;
private long searchHash = -1L;
private long searchPos = -1L;
public Int24Int24MultiMap(long minCapacity) {
capacity = (int) IntIntMultiMap.multiMapCapacity(minCapacity);
capacityMask = capacity - 1;
capacityMod2 = indexToPos(capacity - 1);
long capacity = MultiMapFactory.multiMapCapacity(minCapacity);
this.capacity = (int) capacity;
capacityMask = capacity - 1L;
capacityInBytes = indexToPos(capacity);
bytes = DirectStore.allocateLazy(indexToPos(capacity)).bytes();
positions = newPositions(capacity);
positions = MultiMapFactory.newPositions(capacity);
clear();
}
public Int24Int24MultiMap(Bytes multiMapBytes, Bytes multiMapBitSetBytes) {
capacity = (int) Math.min(MAX_CAPACITY, (multiMapBytes.capacity() / ENTRY_SIZE));
assert capacity <= MAX_CAPACITY;
capacityMask = capacity - 1;
capacityMod2 = indexToPos(capacity - 1);
long capacity = multiMapBytes.capacity() / ENTRY_SIZE;
assert isPowerOf2(capacity);
assert (capacity / 2L) <= MAX_CAPACITY;
this.capacity = (int) capacity;
capacityMask = capacity - 1L;
capacityInBytes = indexToPos(capacity);
this.bytes = multiMapBytes;
positions = new ATSDirectBitSet(multiMapBitSetBytes);
}
@@ -72,29 +74,10 @@ class Int24Int24MultiMap implements MultiMap {
* constructor as the first argument
*/
public static long sizeInBytes(long minCapacity) {
return indexToPos((int) IntIntMultiMap.multiMapCapacity(minCapacity));
return indexToPos(MultiMapFactory.multiMapCapacity(minCapacity));
}
/**
* @param minCapacity as in {@link #Int24Int24MultiMap(long)} constructor
* @return size of {@link Bytes} to provide to {@link #Int24Int24MultiMap(Bytes, Bytes)}
* constructor as the second argument
*/
public static long sizeOfBitSetInBytes(long minCapacity) {
return (int) (Math.max(IntIntMultiMap.multiMapCapacity(minCapacity), 64L) * 6L + 63) / 64;
}
public static ATSDirectBitSet newPositions(int capacity) {
if (!isPowerOf2(capacity))
throw new AssertionError("capacity should be a power of 2");
// bit set size should be at least 1 native long (in bits)
capacity = Math.max(capacity, 64);
// no round-up, because capacity is already a power of 2
int bitSetSizeInBytes = capacity / 8;
return new ATSDirectBitSet(DirectStore.allocateLazy(bitSetSizeInBytes).bytes());
}
private static int indexToPos(int index) {
private static long indexToPos(long index) {
return index * ENTRY_SIZE;
}
@@ -112,35 +95,36 @@ class Int24Int24MultiMap implements MultiMap {
assert positions.isSet(value) : "Shouldn't remove absent value";
}
private static int key(long entry) {
return (int) (entry >>> 24);
private static long key(long entry) {
return entry >>> 24;
}
private static int value(long entry) {
return (int) (entry & MASK);
private static long value(long entry) {
return entry & MASK;
}
private static long entry(long key, long value) {
return (key << 24) | (value & MASK);
}
private int pos(int key) {
private long pos(long key) {
return indexToPos(key & capacityMask);
}
private int step(int pos) {
return (pos + ENTRY_SIZE) % capacityMod2;
private long step(long pos) {
long t;
return (t = (pos += ENTRY_SIZE) - capacityInBytes) < 0L ? pos : t;
}
private int stepBack(int pos) {
return (pos - ENTRY_SIZE) % capacityMod2;
private long stepBack(long pos) {
return (pos -= ENTRY_SIZE) >= 0L ? pos : pos + capacityInBytes;
}
@Override
public void put(long key, long value) {
key = maskUnsetKey(key);
checkValueForPut(value);
for (int pos = pos((int) key); ; pos = step(pos)) {
for (long pos = pos(key); ; pos = step(pos)) {
long entry = bytes.readInt48(pos);
if (entry == UNSET_ENTRY) {
bytes.writeInt48(pos, entry(key, value));
@@ -154,8 +138,8 @@ class Int24Int24MultiMap implements MultiMap {
public void remove(long key, long value) {
key = maskUnsetKey(key);
checkValueForRemove(value);
int posToRemove;
for (int pos = pos((int) key); ; pos = step(pos)) {
long posToRemove;
for (long pos = pos(key); ; pos = step(pos)) {
long entry = bytes.readInt48(pos);
if (key(entry) == key && value(entry) == value) {
posToRemove = pos;
@@ -171,7 +155,7 @@ class Int24Int24MultiMap implements MultiMap {
key = maskUnsetKey(key);
checkValueForRemove(oldValue);
checkValueForPut(newValue);
for (int pos = pos((int) key); ; pos = step(pos)) {
for (long pos = pos(key); ; pos = step(pos)) {
long entry = bytes.readInt48(pos);
if (key(entry) == key && value(entry) == oldValue) {
positions.clear(oldValue);
@@ -182,8 +166,8 @@ class Int24Int24MultiMap implements MultiMap {
}
}
private void removePos(int posToRemove) {
int posToShift = posToRemove;
private void removePos(long posToRemove) {
long posToShift = posToRemove;
while (true) {
posToShift = step(posToShift);
long entryToShift = bytes.readInt48(posToShift);
@@ -214,13 +198,13 @@ class Int24Int24MultiMap implements MultiMap {
@Override
public void startSearch(long key) {
key = maskUnsetKey(key);
searchPos = pos((int) key);
searchHash = (int) key;
searchPos = pos(key);
searchHash = key;
}
@Override
public long nextPos() {
int pos = searchPos;
long pos = searchPos;
while (true) {
long entry = bytes.readInt48(pos);
if (entry == UNSET_ENTRY) {
@@ -237,7 +221,7 @@ class Int24Int24MultiMap implements MultiMap {
@Override
public void removePrevPos() {
int prevPos = stepBack(searchPos);
long prevPos = stepBack(searchPos);
long entry = bytes.readInt48(prevPos);
positions.clear(value(entry));
removePos(prevPos);
@@ -270,7 +254,8 @@ class Int24Int24MultiMap implements MultiMap {
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("{ ");
for (long i = 0L, pos = 0L; i < capacity; i++, pos += ENTRY_SIZE) {
long pos = 0L;
for (int i = 0; i < capacity; i++, pos += ENTRY_SIZE) {
long entry = bytes.readInt48(pos);
if (entry != UNSET_ENTRY)
sb.append(key(entry)).append('=').append(value(entry)).append(", ");
@@ -284,7 +269,8 @@ class Int24Int24MultiMap implements MultiMap {
@Override
public void forEach(EntryConsumer action) {
for (long i = 0L, pos = 0L; i < capacity; i++, pos += ENTRY_SIZE) {
long pos = 0L;
for (int i = 0; i < capacity; i++, pos += ENTRY_SIZE) {
long entry = bytes.readInt48(pos);
if (entry != UNSET_ENTRY)
action.accept(key(entry), value(entry));
@@ -24,6 +24,8 @@ import net.openhft.lang.collection.DirectBitSet;
import net.openhft.lang.io.Bytes;
import net.openhft.lang.io.DirectStore;
import static net.openhft.chronicle.map.MultiMapFactory.multiMapCapacity;
import static net.openhft.chronicle.map.MultiMapFactory.newPositions;
import static net.openhft.lang.Maths.isPowerOf2;
/**
@@ -33,21 +35,11 @@ class IntIntMultiMap implements MultiMap {
static final long MAX_CAPACITY = (1L << 32);
static long multiMapCapacity(long minCapacity) {
if (minCapacity < 0L)
throw new IllegalArgumentException("minCapacity should be positive");
long capacity = Maths.nextPower2(minCapacity, 16L);
if (((double) minCapacity) / capacity > 2./3.) {
// multi map shouldn't be too dense
capacity <<= 1L;
}
return capacity;
}
private static final long ENTRY_SIZE = 8L;
private static final int ENTRY_SIZE_SHIFT = 3;
private static final long UNSET_KEY = 0L;
private static final long HASH_INSTEAD_OF_UNSET_KEY = 0xFFFFFFFFL;
private static final long MASK = 0xFFFFFFFFL;
private static final long HASH_INSTEAD_OF_UNSET_KEY = MASK;
private static final long UNSET_ENTRY = 0L;
final DirectBitSet positions;
@@ -62,7 +54,7 @@ class IntIntMultiMap implements MultiMap {
public IntIntMultiMap(long minCapacity) {
capacity = multiMapCapacity(minCapacity);
capacityMask = capacity - 1L;
capacityMask2 = indexToPos(capacity - 1L);
capacityMask2 = indexToPos(capacityMask);
bytes = DirectStore.allocateLazy(indexToPos(capacity)).bytes();
positions = newPositions(capacity);
clear();
@@ -70,9 +62,10 @@ class IntIntMultiMap implements MultiMap {
public IntIntMultiMap(Bytes multiMapBytes, Bytes multiMapBitSetBytes) {
capacity = multiMapBytes.capacity() / ENTRY_SIZE;
assert capacity == Maths.nextPower2(capacity, 16L);
assert isPowerOf2(capacity);
assert (capacity / 2L) <= MAX_CAPACITY;
capacityMask = capacity - 1L;
capacityMask2 = indexToPos(capacity - 1L);
capacityMask2 = indexToPos(capacityMask);
this.bytes = multiMapBytes;
positions = new ATSDirectBitSet(multiMapBitSetBytes);
}
@@ -86,40 +79,21 @@ class IntIntMultiMap implements MultiMap {
return indexToPos(multiMapCapacity(minCapacity));
}
/**
* @param minCapacity as in {@link #IntIntMultiMap(long)} constructor
* @return size of {@link Bytes} to provide to {@link #IntIntMultiMap(Bytes, Bytes)}
* constructor as the second argument
*/
public static long sizeOfBitSetInBytes(long minCapacity) {
return Math.max(multiMapCapacity(minCapacity), 64L) / 8L;
}
public static ATSDirectBitSet newPositions(long capacity) {
if (!isPowerOf2(capacity))
throw new AssertionError("capacity should be a power of 2");
// bit set size should be at least 1 native long (in bits)
capacity = Math.max(capacity, 64L);
// no round-up, because capacity is already a power of 2
long bitSetSizeInBytes = capacity / 8L;
return new ATSDirectBitSet(DirectStore.allocateLazy(bitSetSizeInBytes).bytes());
}
private static long indexToPos(long index) {
return index << ENTRY_SIZE_SHIFT;
}
private static long maskUnsetKey(long key) {
return (key &= 0xFFFFFFFFL) != UNSET_KEY ? key : HASH_INSTEAD_OF_UNSET_KEY;
return (key &= MASK) != UNSET_KEY ? key : HASH_INSTEAD_OF_UNSET_KEY;
}
private void checkValueForPut(long value) {
assert (value & ~0xFFFFFFFFL) == 0L : "Value out of range, was " + value;
assert (value & ~MASK) == 0L : "Value out of range, was " + value;
assert positions.isClear(value) : "Shouldn't put existing value";
}
private void checkValueForRemove(long value) {
assert (value & ~0xFFFFFFFFL) == 0L : "Value out of range, was " + value;
assert (value & ~MASK) == 0L : "Value out of range, was " + value;
assert positions.isSet(value) : "Shouldn't remove absent value";
}
@@ -128,7 +102,7 @@ class IntIntMultiMap implements MultiMap {
}
private static long value(long entry) {
return entry & 0xFFFFFFFFL;
return entry & MASK;
}
private static long entry(long key, long value) {
@@ -18,8 +18,16 @@
package net.openhft.chronicle.map;
import net.openhft.lang.Maths;
import net.openhft.lang.collection.ATSDirectBitSet;
import net.openhft.lang.io.DirectStore;
import net.openhft.lang.io.NativeBytes;
import static net.openhft.lang.Maths.isPowerOf2;
import static net.openhft.lang.MemoryUnit.BITS;
import static net.openhft.lang.MemoryUnit.BYTES;
import static net.openhft.lang.MemoryUnit.LONGS;
/**
* Created by peter on 09/11/14.
*
@@ -32,11 +40,6 @@ enum MultiMapFactory {
return ShortShortMultiMap.sizeInBytes(minCapacity);
}
@Override
public long sizeOfBitSetInBytes(long minCapacity) {
return ShortShortMultiMap.sizeOfBitSetInBytes(minCapacity);
}
@Override
public MultiMap create(NativeBytes bytes, NativeBytes bitSetBytes) {
return new ShortShortMultiMap(bytes, bitSetBytes);
@@ -48,11 +51,6 @@ enum MultiMapFactory {
return Int24Int24MultiMap.sizeInBytes(minCapacity);
}
@Override
public long sizeOfBitSetInBytes(long minCapacity) {
return Int24Int24MultiMap.sizeOfBitSetInBytes(minCapacity);
}
@Override
public MultiMap create(NativeBytes bytes, NativeBytes bitSetBytes) {
return new Int24Int24MultiMap(bytes, bitSetBytes);
@@ -64,11 +62,6 @@ enum MultiMapFactory {
return IntIntMultiMap.sizeInBytes(minCapacity);
}
@Override
public long sizeOfBitSetInBytes(long minCapacity) {
return IntIntMultiMap.sizeOfBitSetInBytes(minCapacity);
}
@Override
public MultiMap create(NativeBytes bytes, NativeBytes bitSetBytes) {
return new IntIntMultiMap(bytes, bitSetBytes);
@@ -88,9 +81,29 @@ enum MultiMapFactory {
throw new IllegalArgumentException("Capacity " + capacity + " not supported");
}
public abstract long sizeInBytes(long minCapacity);
public static long sizeOfBitSetInBytes(long minCapacity) {
return BYTES.convert(LONGS.align(multiMapCapacity(minCapacity), BITS), BITS);
}
public abstract long sizeOfBitSetInBytes(long minCapacity);
public static ATSDirectBitSet newPositions(long capacity) {
if (!isPowerOf2(capacity))
throw new AssertionError("capacity should be a power of 2");
capacity = LONGS.align(capacity, BITS);
return new ATSDirectBitSet(DirectStore.allocateLazy(BYTES.convert(capacity, BITS)).bytes());
}
static long multiMapCapacity(long minCapacity) {
if (minCapacity < 0L)
throw new IllegalArgumentException("minCapacity should be positive");
long capacity = Maths.nextPower2(minCapacity, 16L);
if (((double) minCapacity) / capacity > 2./3.) {
// multi map shouldn't be too dense
capacity <<= 1L;
}
return capacity;
}
public abstract long sizeInBytes(long minCapacity);
public abstract MultiMap create(NativeBytes bytes, NativeBytes bitSetBytes);
}
@@ -24,7 +24,8 @@ import net.openhft.lang.collection.DirectBitSet;
import net.openhft.lang.io.Bytes;
import net.openhft.lang.io.DirectStore;
import static net.openhft.chronicle.map.IntIntMultiMap.multiMapCapacity;
import static net.openhft.chronicle.map.MultiMapFactory.multiMapCapacity;
import static net.openhft.chronicle.map.MultiMapFactory.newPositions;
import static net.openhft.lang.Maths.isPowerOf2;
/**
@@ -37,7 +38,8 @@ class ShortShortMultiMap implements MultiMap {
private static final long ENTRY_SIZE = 4L;
private static final int ENTRY_SIZE_SHIFT = 2;
private static final int UNSET_KEY = 0;
private static final long HASH_INSTEAD_OF_UNSET_KEY = 0xFFFFL;
private static final long MASK = 0xFFFFL;
private static final long HASH_INSTEAD_OF_UNSET_KEY = MASK;
private static final int UNSET_ENTRY = 0;
private final int capacity;
private final long capacityMask;
@@ -53,9 +55,9 @@ class ShortShortMultiMap implements MultiMap {
assert isPowerOf2(capacity);
this.capacity = (int) capacity;
capacityMask = capacity - 1L;
capacityMask2 = (capacity - 1L) * ENTRY_SIZE;
capacityMask2 = capacityMask * ENTRY_SIZE;
bytes = DirectStore.allocateLazy(capacity * ENTRY_SIZE).bytes();
positions = IntIntMultiMap.newPositions(capacity);
positions = newPositions(capacity);
clear();
}
@@ -65,7 +67,7 @@ class ShortShortMultiMap implements MultiMap {
assert (capacity / 2L) <= MAX_CAPACITY;
this.capacity = (int) capacity;
capacityMask = capacity - 1L;
capacityMask2 = (capacity - 1L) * ENTRY_SIZE;
capacityMask2 = capacityMask * ENTRY_SIZE;
this.bytes = multiMapBytes;
positions = new ATSDirectBitSet(multiMapBitSetBytes);
}
@@ -79,30 +81,19 @@ class ShortShortMultiMap implements MultiMap {
return multiMapCapacity(minCapacity) * ENTRY_SIZE;
}
/**
* @param minCapacity as in {@link #ShortShortMultiMap(long)} constructor
* @return size of {@link Bytes} to provide to {@link #ShortShortMultiMap(Bytes, Bytes)}
* constructor as the second argument
*/
public static long sizeOfBitSetInBytes(long minCapacity) {
return IntIntMultiMap.sizeOfBitSetInBytes(minCapacity);
}
private void checkValueForPut(long value) {
assert (value & ~0xFFFFL) == 0L : "Value out of range, was " + value;
assert (value & ~MASK) == 0L : "Value out of range, was " + value;
assert positions.isClear(value) : "Shouldn't put existing value";
}
private void checkValueForRemove(long value) {
assert (value & ~0xFFFFL) == 0L : "Value out of range, was " + value;
assert (value & ~MASK) == 0L : "Value out of range, was " + value;
assert positions.isSet(value) : "Shouldn't remove absent value";
}
private static long maskUnsetKey(long key) {
return (key &= 0xFFFFL) != UNSET_KEY ? key : HASH_INSTEAD_OF_UNSET_KEY;
return (key &= MASK) != UNSET_KEY ? key : HASH_INSTEAD_OF_UNSET_KEY;
}
private long step(long pos) {
@@ -252,7 +252,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
}
long sizeOfMultiMapBitSet() {
return multiMapFactory.sizeOfBitSetInBytes(entriesPerSegment);
return MultiMapFactory.sizeOfBitSetInBytes(entriesPerSegment);
}
long sizeOfBitSets() {