From 7e6c361d4e03b58759a41fb7059dc22a030b3ad5 Mon Sep 17 00:00:00 2001 From: leventov Date: Tue, 11 Nov 2014 12:09:52 +0300 Subject: [PATCH] Cleaned up MultiMaps --- .../chronicle/map/Int24Int24MultiMap.java | 104 ++++++++---------- .../openhft/chronicle/map/IntIntMultiMap.java | 50 ++------- .../chronicle/map/MultiMapFactory.java | 47 +++++--- .../chronicle/map/ShortShortMultiMap.java | 29 ++--- .../chronicle/map/VanillaChronicleMap.java | 2 +- 5 files changed, 98 insertions(+), 134 deletions(-) diff --git a/src/main/java/net/openhft/chronicle/map/Int24Int24MultiMap.java b/src/main/java/net/openhft/chronicle/map/Int24Int24MultiMap.java index c2f0c3bed..f0255a4e0 100644 --- a/src/main/java/net/openhft/chronicle/map/Int24Int24MultiMap.java +++ b/src/main/java/net/openhft/chronicle/map/Int24Int24MultiMap.java @@ -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)); diff --git a/src/main/java/net/openhft/chronicle/map/IntIntMultiMap.java b/src/main/java/net/openhft/chronicle/map/IntIntMultiMap.java index 9f0b0c113..e441a14f8 100644 --- a/src/main/java/net/openhft/chronicle/map/IntIntMultiMap.java +++ b/src/main/java/net/openhft/chronicle/map/IntIntMultiMap.java @@ -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) { diff --git a/src/main/java/net/openhft/chronicle/map/MultiMapFactory.java b/src/main/java/net/openhft/chronicle/map/MultiMapFactory.java index 3c5ab1e7e..d88de2fb7 100644 --- a/src/main/java/net/openhft/chronicle/map/MultiMapFactory.java +++ b/src/main/java/net/openhft/chronicle/map/MultiMapFactory.java @@ -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); } diff --git a/src/main/java/net/openhft/chronicle/map/ShortShortMultiMap.java b/src/main/java/net/openhft/chronicle/map/ShortShortMultiMap.java index 7bd73eecf..f5ce4b7f5 100644 --- a/src/main/java/net/openhft/chronicle/map/ShortShortMultiMap.java +++ b/src/main/java/net/openhft/chronicle/map/ShortShortMultiMap.java @@ -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) { diff --git a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java index 0189ea22f..a7b8932d5 100644 --- a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java @@ -252,7 +252,7 @@ class VanillaChronicleMap, } long sizeOfMultiMapBitSet() { - return multiMapFactory.sizeOfBitSetInBytes(entriesPerSegment); + return MultiMapFactory.sizeOfBitSetInBytes(entriesPerSegment); } long sizeOfBitSets() {