mirror of
https://github.com/OpenHFT/Chronicle-Map.git
synced 2021-09-13 01:26:12 +00:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -46,7 +46,7 @@
|
||||
<dependency>
|
||||
<groupId>net.openhft</groupId>
|
||||
<artifactId>lang</artifactId>
|
||||
<version>6.4.12</version>
|
||||
<version>6.4.13-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -26,7 +26,7 @@ import net.openhft.chronicle.hash.serialization.SizeMarshaller;
|
||||
import net.openhft.chronicle.hash.serialization.internal.MetaBytesInterop;
|
||||
import net.openhft.chronicle.hash.serialization.internal.MetaBytesWriter;
|
||||
import net.openhft.chronicle.hash.serialization.internal.MetaProvider;
|
||||
import net.openhft.chronicle.map.utils.MultiMapFactory;
|
||||
import net.openhft.chronicle.map.MultiMapFactory;
|
||||
import net.openhft.chronicle.set.ChronicleSetBuilder;
|
||||
import net.openhft.lang.Maths;
|
||||
import net.openhft.lang.io.ByteBufferBytes;
|
||||
@@ -372,10 +372,8 @@ public abstract class AbstractChronicleMapBuilder<K, V,
|
||||
if (segments == 1)
|
||||
return entries;
|
||||
double poorDistEntriesScale = Math.log(segments) * entries;
|
||||
if (segments <= 8)
|
||||
return Math.min(entries * segments,
|
||||
(long) (entries + poorDistEntriesScale * 0.11 + segments * 8)); // 11% was min for tests
|
||||
return (long) (entries + poorDistEntriesScale * 0.11 + 80); // 11% was min for tests
|
||||
return Math.min(entries * segments,
|
||||
(long) (entries + poorDistEntriesScale * 0.14 + 32)); // 12% was min for tests
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -390,24 +388,22 @@ public abstract class AbstractChronicleMapBuilder<K, V,
|
||||
}
|
||||
|
||||
private int estimateSegments() {
|
||||
for (int power = 1; power < 64; power *= 2) {
|
||||
if (entries <= power * power * power)
|
||||
return power;
|
||||
}
|
||||
long size = (entrySize + 32) * entries;
|
||||
if (size < 10L * 1000 * 1000) // 10 MB
|
||||
return 64;
|
||||
if (size < 100L * 1000 * 1000) // 100 MB
|
||||
return 128;
|
||||
if (size < 400L * 1000 * 1000) // 400 MB
|
||||
return 256;
|
||||
if (size < 1L * 1000 * 1000 * 1000) // 1 GB
|
||||
return (int) Math.min(Maths.nextPower2(entries / 32, 1), estimateSegementBasedOnSize());
|
||||
|
||||
}
|
||||
|
||||
private int estimateSegementBasedOnSize() {
|
||||
// based on entries with multimap of 100 bytes.
|
||||
long size = (long) (Math.log10(entrySize + 32) / 2 * entries);
|
||||
if (size < 10 * 1000)
|
||||
return 512;
|
||||
if (size < 10L * 1000 * 1000 * 1000) // 10 GB
|
||||
if (size < 100 * 1000)
|
||||
return 1024;
|
||||
if (size < 100L * 1000 * 1000 * 1000) // 100 GB
|
||||
if (size < 1000 * 1000)
|
||||
return 2048;
|
||||
return 4096;
|
||||
if (size < 10 * 1000 * 1000)
|
||||
return 4096;
|
||||
return 8192;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -439,6 +435,17 @@ public abstract class AbstractChronicleMapBuilder<K, V,
|
||||
return segments <= maxSegments ? segments : -segments;
|
||||
}
|
||||
|
||||
|
||||
int segmentHeaderSize() {
|
||||
int segments = actualSegments();
|
||||
// reduce false sharing unless we have a lot of segments.
|
||||
return segments <= 8192 ? 128 : 64;
|
||||
}
|
||||
|
||||
MultiMapFactory multiMapFactory() {
|
||||
return MultiMapFactory.forCapacity(actualEntriesPerSegment());
|
||||
}
|
||||
|
||||
public B lockTimeOut(long lockTimeOut, TimeUnit unit) {
|
||||
this.lockTimeOut = lockTimeOut;
|
||||
lockTimeOutUnit = unit;
|
||||
@@ -933,15 +940,5 @@ public abstract class AbstractChronicleMapBuilder<K, V,
|
||||
return (int) Math.min(Math.max(2L, entries() >> 10),
|
||||
VanillaChronicleMap.MAX_ENTRY_OVERSIZE_FACTOR);
|
||||
}
|
||||
|
||||
int segmentHeaderSize() {
|
||||
int segments = actualSegments();
|
||||
// reduce false sharing unless we have a lot of segments.
|
||||
return segments <= 8192 ? 64 : 16;
|
||||
}
|
||||
|
||||
public MultiMapFactory multiMapFactory() {
|
||||
return MultiMapFactory.forCapacity(actualEntriesPerSegment());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-11
@@ -16,7 +16,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.openhft.chronicle.map.utils;
|
||||
package net.openhft.chronicle.map;
|
||||
|
||||
import net.openhft.lang.collection.ATSDirectBitSet;
|
||||
import net.openhft.lang.collection.DirectBitSet;
|
||||
@@ -28,7 +28,7 @@ import static net.openhft.lang.Maths.isPowerOf2;
|
||||
/**
|
||||
* Supports a simple interface for int -> int[] off heap.
|
||||
*/
|
||||
class VanillaI24I24MultiMap implements MultiMap {
|
||||
class Int24Int24MultiMap implements MultiMap {
|
||||
|
||||
static final int MAX_CAPACITY = (1 << 24);
|
||||
|
||||
@@ -48,8 +48,8 @@ class VanillaI24I24MultiMap implements MultiMap {
|
||||
private int searchHash = -1;
|
||||
private int searchPos = -1;
|
||||
|
||||
public VanillaI24I24MultiMap(long minCapacity) {
|
||||
capacity = (int) VanillaIntIntMultiMap.multiMapCapacity(minCapacity);
|
||||
public Int24Int24MultiMap(long minCapacity) {
|
||||
capacity = (int) IntIntMultiMap.multiMapCapacity(minCapacity);
|
||||
capacityMask = capacity - 1;
|
||||
capacityMod2 = indexToPos(capacity - 1);
|
||||
bytes = DirectStore.allocateLazy(indexToPos(capacity)).bytes();
|
||||
@@ -57,7 +57,7 @@ class VanillaI24I24MultiMap implements MultiMap {
|
||||
clear();
|
||||
}
|
||||
|
||||
public VanillaI24I24MultiMap(Bytes multiMapBytes, Bytes multiMapBitSetBytes) {
|
||||
public Int24Int24MultiMap(Bytes multiMapBytes, Bytes multiMapBitSetBytes) {
|
||||
capacity = (int) Math.min(MAX_CAPACITY, (multiMapBytes.capacity() / ENTRY_SIZE));
|
||||
assert capacity <= MAX_CAPACITY;
|
||||
capacityMask = capacity - 1;
|
||||
@@ -67,21 +67,21 @@ class VanillaI24I24MultiMap implements MultiMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param minCapacity as in {@link #VanillaI24I24MultiMap(long)} constructor
|
||||
* @return size of {@link Bytes} to provide to {@link #VanillaI24I24MultiMap(Bytes, Bytes)}
|
||||
* @param minCapacity as in {@link #Int24Int24MultiMap(long)} constructor
|
||||
* @return size of {@link Bytes} to provide to {@link #Int24Int24MultiMap(Bytes, Bytes)}
|
||||
* constructor as the first argument
|
||||
*/
|
||||
public static long sizeInBytes(long minCapacity) {
|
||||
return indexToPos((int) VanillaIntIntMultiMap.multiMapCapacity(minCapacity));
|
||||
return indexToPos((int) IntIntMultiMap.multiMapCapacity(minCapacity));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param minCapacity as in {@link #VanillaI24I24MultiMap(long)} constructor
|
||||
* @return size of {@link Bytes} to provide to {@link #VanillaI24I24MultiMap(Bytes, Bytes)}
|
||||
* @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(VanillaIntIntMultiMap.multiMapCapacity(minCapacity), 64L) * 6L + 63) / 64;
|
||||
return (int) (Math.max(IntIntMultiMap.multiMapCapacity(minCapacity), 64L) * 6L + 63) / 64;
|
||||
}
|
||||
|
||||
public static ATSDirectBitSet newPositions(int capacity) {
|
||||
+8
-8
@@ -16,7 +16,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.openhft.chronicle.map.utils;
|
||||
package net.openhft.chronicle.map;
|
||||
|
||||
import net.openhft.lang.Maths;
|
||||
import net.openhft.lang.collection.ATSDirectBitSet;
|
||||
@@ -29,7 +29,7 @@ import static net.openhft.lang.Maths.isPowerOf2;
|
||||
/**
|
||||
* Supports a simple interface for int -> int[] off heap.
|
||||
*/
|
||||
class VanillaIntIntMultiMap implements MultiMap {
|
||||
class IntIntMultiMap implements MultiMap {
|
||||
|
||||
static final long MAX_CAPACITY = (1L << 32);
|
||||
|
||||
@@ -59,7 +59,7 @@ class VanillaIntIntMultiMap implements MultiMap {
|
||||
private long searchHash = -1L;
|
||||
private long searchPos = -1L;
|
||||
|
||||
public VanillaIntIntMultiMap(long minCapacity) {
|
||||
public IntIntMultiMap(long minCapacity) {
|
||||
capacity = multiMapCapacity(minCapacity);
|
||||
capacityMask = capacity - 1L;
|
||||
capacityMask2 = indexToPos(capacity - 1L);
|
||||
@@ -68,7 +68,7 @@ class VanillaIntIntMultiMap implements MultiMap {
|
||||
clear();
|
||||
}
|
||||
|
||||
public VanillaIntIntMultiMap(Bytes multiMapBytes, Bytes multiMapBitSetBytes) {
|
||||
public IntIntMultiMap(Bytes multiMapBytes, Bytes multiMapBitSetBytes) {
|
||||
capacity = multiMapBytes.capacity() / ENTRY_SIZE;
|
||||
assert capacity == Maths.nextPower2(capacity, 16L);
|
||||
capacityMask = capacity - 1L;
|
||||
@@ -78,8 +78,8 @@ class VanillaIntIntMultiMap implements MultiMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param minCapacity as in {@link #VanillaIntIntMultiMap(long)} constructor
|
||||
* @return size of {@link Bytes} to provide to {@link #VanillaIntIntMultiMap(Bytes, Bytes)}
|
||||
* @param minCapacity as in {@link #IntIntMultiMap(long)} constructor
|
||||
* @return size of {@link Bytes} to provide to {@link #IntIntMultiMap(Bytes, Bytes)}
|
||||
* constructor as the first argument
|
||||
*/
|
||||
public static long sizeInBytes(long minCapacity) {
|
||||
@@ -87,8 +87,8 @@ class VanillaIntIntMultiMap implements MultiMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param minCapacity as in {@link #VanillaIntIntMultiMap(long)} constructor
|
||||
* @return size of {@link Bytes} to provide to {@link #VanillaIntIntMultiMap(Bytes, Bytes)}
|
||||
* @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) {
|
||||
+2
-2
@@ -14,14 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.openhft.chronicle.map.utils;
|
||||
package net.openhft.chronicle.map;
|
||||
|
||||
import net.openhft.lang.collection.DirectBitSet;
|
||||
|
||||
/**
|
||||
* This is only used to store keys and positions, but it could store int/int key/values for another purpose.
|
||||
*/
|
||||
public interface MultiMap {
|
||||
interface MultiMap {
|
||||
|
||||
/**
|
||||
* @return gets all the active positions as a bitset
|
||||
+16
-16
@@ -16,7 +16,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.openhft.chronicle.map.utils;
|
||||
package net.openhft.chronicle.map;
|
||||
|
||||
import net.openhft.lang.io.NativeBytes;
|
||||
|
||||
@@ -25,65 +25,65 @@ import net.openhft.lang.io.NativeBytes;
|
||||
*
|
||||
* In future we might support I32 -> I16 or similar.
|
||||
*/
|
||||
public enum MultiMapFactory {
|
||||
enum MultiMapFactory {
|
||||
I16 {
|
||||
@Override
|
||||
public long sizeInBytes(long minCapacity) {
|
||||
return VanillaShortShortMultiMap.sizeInBytes(minCapacity);
|
||||
return ShortShortMultiMap.sizeInBytes(minCapacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long sizeOfBitSetInBytes(long minCapacity) {
|
||||
return VanillaShortShortMultiMap.sizeOfBitSetInBytes(minCapacity);
|
||||
return ShortShortMultiMap.sizeOfBitSetInBytes(minCapacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiMap create(NativeBytes bytes, NativeBytes bitSetBytes) {
|
||||
return new VanillaShortShortMultiMap(bytes, bitSetBytes);
|
||||
return new ShortShortMultiMap(bytes, bitSetBytes);
|
||||
}
|
||||
},
|
||||
I24 {
|
||||
@Override
|
||||
public long sizeInBytes(long minCapacity) {
|
||||
return VanillaI24I24MultiMap.sizeInBytes(minCapacity);
|
||||
return Int24Int24MultiMap.sizeInBytes(minCapacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long sizeOfBitSetInBytes(long minCapacity) {
|
||||
return VanillaI24I24MultiMap.sizeOfBitSetInBytes(minCapacity);
|
||||
return Int24Int24MultiMap.sizeOfBitSetInBytes(minCapacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiMap create(NativeBytes bytes, NativeBytes bitSetBytes) {
|
||||
return new VanillaI24I24MultiMap(bytes, bitSetBytes);
|
||||
return new Int24Int24MultiMap(bytes, bitSetBytes);
|
||||
}
|
||||
},
|
||||
I32 {
|
||||
@Override
|
||||
public long sizeInBytes(long minCapacity) {
|
||||
return VanillaIntIntMultiMap.sizeInBytes(minCapacity);
|
||||
return IntIntMultiMap.sizeInBytes(minCapacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long sizeOfBitSetInBytes(long minCapacity) {
|
||||
return VanillaIntIntMultiMap.sizeOfBitSetInBytes(minCapacity);
|
||||
return IntIntMultiMap.sizeOfBitSetInBytes(minCapacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiMap create(NativeBytes bytes, NativeBytes bitSetBytes) {
|
||||
return new VanillaIntIntMultiMap(bytes, bitSetBytes);
|
||||
return new IntIntMultiMap(bytes, bitSetBytes);
|
||||
}
|
||||
};
|
||||
|
||||
public static final long I16_MAX_CAPACITY = VanillaShortShortMultiMap.MAX_CAPACITY;
|
||||
public static final long MAX_CAPACITY = VanillaIntIntMultiMap.MAX_CAPACITY;
|
||||
public static final long I16_MAX_CAPACITY = ShortShortMultiMap.MAX_CAPACITY;
|
||||
public static final long MAX_CAPACITY = IntIntMultiMap.MAX_CAPACITY;
|
||||
|
||||
public static MultiMapFactory forCapacity(long capacity) {
|
||||
if (capacity <= VanillaShortShortMultiMap.MAX_CAPACITY)
|
||||
if (capacity <= ShortShortMultiMap.MAX_CAPACITY)
|
||||
return I16;
|
||||
// if (capacity <= VanillaI24I24MultiMap.MAX_CAPACITY)
|
||||
// if (capacity <= Int24Int24MultiMap.MAX_CAPACITY)
|
||||
// return I24;
|
||||
if (capacity <= VanillaIntIntMultiMap.MAX_CAPACITY)
|
||||
if (capacity <= IntIntMultiMap.MAX_CAPACITY)
|
||||
return I32;
|
||||
throw new IllegalArgumentException("Capacity " + capacity + " not supported");
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import net.openhft.chronicle.hash.replication.TimeProvider;
|
||||
import net.openhft.chronicle.hash.serialization.BytesReader;
|
||||
import net.openhft.chronicle.hash.serialization.internal.MetaBytesInterop;
|
||||
import net.openhft.chronicle.hash.serialization.internal.MetaBytesWriter;
|
||||
import net.openhft.chronicle.map.utils.MultiMap;
|
||||
import net.openhft.chronicle.map.MultiMap;
|
||||
import net.openhft.lang.Maths;
|
||||
import net.openhft.lang.collection.ATSDirectBitSet;
|
||||
import net.openhft.lang.io.Bytes;
|
||||
@@ -273,7 +273,7 @@ final class ReplicatedChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
}
|
||||
|
||||
@Override
|
||||
<T extends Context> T lookupUsing(K key, V value, LockType lockTypeType) {
|
||||
<T extends Context> T lookupUsing(K key, V usingValue, LockType lockType, boolean mustReuseValue) {
|
||||
checkKey(key);
|
||||
ThreadLocalCopies copies = keyInteropProvider.getCopies(null);
|
||||
KI keyWriter = keyInteropProvider.get(copies, originalKeyInterop);
|
||||
@@ -284,24 +284,26 @@ final class ReplicatedChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
int segmentNum = getSegment(hash);
|
||||
long segmentHash = segmentHash(hash);
|
||||
|
||||
boolean create = lockTypeType == LockType.WRITE_LOCK;
|
||||
boolean create = lockType == LockType.WRITE_LOCK;
|
||||
Segment segment = segment(segmentNum);
|
||||
|
||||
MutableLockedEntry<K, V, MKI, KI> lock = (lockTypeType == LockType.WRITE_LOCK)
|
||||
MutableLockedEntry<K, V, MKI, KI> lock = (lockType == LockType.WRITE_LOCK)
|
||||
? segment.writeLock()
|
||||
: segment.readLock();
|
||||
|
||||
V v = segment.acquireWithoutLock(copies, metaKeyWriter, keyWriter, key,
|
||||
value,
|
||||
usingValue,
|
||||
segmentHash, create, create ? timeProvider.currentTimeMillis() : 0L);
|
||||
|
||||
checkReallyUsingValue(usingValue, mustReuseValue, v);
|
||||
|
||||
lock.copies = copies;
|
||||
lock.metaKeyInterop = metaKeyWriter;
|
||||
lock.keyInterop = keyWriter;
|
||||
lock.segmentHash = segmentHash;
|
||||
|
||||
if (!isNativeValueClass && lockTypeType == LockType.WRITE_LOCK && v == null)
|
||||
v = value;
|
||||
if (!isNativeValueClass && lockType == LockType.WRITE_LOCK && v == null)
|
||||
v = usingValue;
|
||||
|
||||
lock.value(v);
|
||||
lock.key(key);
|
||||
|
||||
+12
-12
@@ -16,7 +16,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.openhft.chronicle.map.utils;
|
||||
package net.openhft.chronicle.map;
|
||||
|
||||
|
||||
import net.openhft.lang.collection.ATSDirectBitSet;
|
||||
@@ -24,13 +24,13 @@ import net.openhft.lang.collection.DirectBitSet;
|
||||
import net.openhft.lang.io.Bytes;
|
||||
import net.openhft.lang.io.DirectStore;
|
||||
|
||||
import static net.openhft.chronicle.map.utils.VanillaIntIntMultiMap.multiMapCapacity;
|
||||
import static net.openhft.chronicle.map.IntIntMultiMap.multiMapCapacity;
|
||||
import static net.openhft.lang.Maths.isPowerOf2;
|
||||
|
||||
/**
|
||||
* Supports a simple interface for int -> int[] off heap.
|
||||
*/
|
||||
class VanillaShortShortMultiMap implements MultiMap {
|
||||
class ShortShortMultiMap implements MultiMap {
|
||||
|
||||
static final long MAX_CAPACITY = (1L << 16);
|
||||
|
||||
@@ -47,7 +47,7 @@ class VanillaShortShortMultiMap implements MultiMap {
|
||||
private long searchHash = -1L;
|
||||
private long searchPos = -1L;
|
||||
|
||||
public VanillaShortShortMultiMap(long minCapacity) {
|
||||
public ShortShortMultiMap(long minCapacity) {
|
||||
assert minCapacity <= MAX_CAPACITY;
|
||||
long capacity = multiMapCapacity(minCapacity);
|
||||
assert isPowerOf2(capacity);
|
||||
@@ -55,11 +55,11 @@ class VanillaShortShortMultiMap implements MultiMap {
|
||||
capacityMask = capacity - 1L;
|
||||
capacityMask2 = (capacity - 1L) * ENTRY_SIZE;
|
||||
bytes = DirectStore.allocateLazy(capacity * ENTRY_SIZE).bytes();
|
||||
positions = VanillaIntIntMultiMap.newPositions(capacity);
|
||||
positions = IntIntMultiMap.newPositions(capacity);
|
||||
clear();
|
||||
}
|
||||
|
||||
public VanillaShortShortMultiMap(Bytes multiMapBytes, Bytes multiMapBitSetBytes) {
|
||||
public ShortShortMultiMap(Bytes multiMapBytes, Bytes multiMapBitSetBytes) {
|
||||
long capacity = multiMapBytes.capacity() / ENTRY_SIZE;
|
||||
assert isPowerOf2(capacity);
|
||||
assert (capacity / 2L) <= MAX_CAPACITY;
|
||||
@@ -71,8 +71,8 @@ class VanillaShortShortMultiMap implements MultiMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param minCapacity as in {@link #VanillaShortShortMultiMap(long)} constructor
|
||||
* @return size of {@link Bytes} to provide to {@link #VanillaShortShortMultiMap(Bytes, Bytes)}
|
||||
* @param minCapacity as in {@link #ShortShortMultiMap(long)} constructor
|
||||
* @return size of {@link Bytes} to provide to {@link #ShortShortMultiMap(Bytes, Bytes)}
|
||||
* constructor as the first argument
|
||||
*/
|
||||
public static long sizeInBytes(long minCapacity) {
|
||||
@@ -80,14 +80,14 @@ class VanillaShortShortMultiMap implements MultiMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param minCapacity as in {@link #VanillaShortShortMultiMap(long)} constructor
|
||||
* @return size of {@link Bytes} to provide to {@link #VanillaShortShortMultiMap(Bytes, Bytes)}
|
||||
* @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 VanillaIntIntMultiMap.sizeOfBitSetInBytes(minCapacity);
|
||||
return IntIntMultiMap.sizeOfBitSetInBytes(minCapacity);
|
||||
}
|
||||
|
||||
private void checkValueForPut(long value) {
|
||||
@@ -183,7 +183,7 @@ class VanillaShortShortMultiMap implements MultiMap {
|
||||
if (entryToShift == UNSET_ENTRY)
|
||||
break;
|
||||
long insertPos = pos(key(entryToShift));
|
||||
// see comment in VanillaIntIntMultiMap
|
||||
// see comment in IntIntMultiMap
|
||||
boolean cond1 = insertPos <= posToRemove;
|
||||
boolean cond2 = posToRemove <= posToShift;
|
||||
if ((cond1 && cond2) ||
|
||||
@@ -25,8 +25,6 @@ import net.openhft.chronicle.hash.serialization.SizeMarshaller;
|
||||
import net.openhft.chronicle.hash.serialization.internal.MetaBytesInterop;
|
||||
import net.openhft.chronicle.hash.serialization.internal.MetaBytesWriter;
|
||||
import net.openhft.chronicle.hash.serialization.internal.MetaProvider;
|
||||
import net.openhft.chronicle.map.utils.MultiMap;
|
||||
import net.openhft.chronicle.map.utils.MultiMapFactory;
|
||||
import net.openhft.lang.collection.DirectBitSet;
|
||||
import net.openhft.lang.collection.SingleThreadedDirectBitSet;
|
||||
import net.openhft.lang.io.*;
|
||||
@@ -357,7 +355,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
public V get(Object key) {
|
||||
|
||||
try (Context<K, V> entry = lookupUsing((K) key, null,
|
||||
LockType.READ_LOCK)) {
|
||||
LockType.READ_LOCK, false)) {
|
||||
return entry.value();
|
||||
}
|
||||
}
|
||||
@@ -366,7 +364,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
public V getUsing(K key, V usingValue) {
|
||||
|
||||
try (Context<K, V> entry = lookupUsing(key, usingValue,
|
||||
LockType.READ_LOCK)) {
|
||||
LockType.READ_LOCK, false)) {
|
||||
return entry.value();
|
||||
}
|
||||
}
|
||||
@@ -374,20 +372,21 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
@NotNull
|
||||
@Override
|
||||
public WriteContext<K, V> acquireUsingLocked(@NotNull K key, @NotNull V usingValue) {
|
||||
return this.lookupUsing(key, usingValue, LockType.WRITE_LOCK);
|
||||
return this.lookupUsing(key, usingValue, LockType.WRITE_LOCK, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ReadContext<K, V> getUsingLocked(@NotNull K key, @NotNull V usingValue) {
|
||||
return this.lookupUsing(key, usingValue, LockType.READ_LOCK);
|
||||
return this.lookupUsing(key, usingValue, LockType.READ_LOCK, true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public V acquireUsing(@NotNull K key, @NotNull V usingValue) {
|
||||
|
||||
try (WriteContext<K, V> kvContext = lookupUsing(key, usingValue, LockType.WRITE_LOCK)) {
|
||||
try (WriteContext<K, V> kvContext =
|
||||
lookupUsing(key, usingValue, LockType.WRITE_LOCK, false)) {
|
||||
|
||||
if (!isNativeValueClass)
|
||||
kvContext.dontPutOnClose();
|
||||
@@ -397,7 +396,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
}
|
||||
|
||||
|
||||
<T extends Context> T lookupUsing(K key, V value, LockType lockTypeType) {
|
||||
<T extends Context> T lookupUsing(K key, V usingValue, LockType lockType, boolean mustReuseValue) {
|
||||
checkKey(key);
|
||||
ThreadLocalCopies copies = keyInteropProvider.getCopies(null);
|
||||
KI keyInterop = keyInteropProvider.get(copies, originalKeyInterop);
|
||||
@@ -409,20 +408,22 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
long segmentHash = segmentHash(hash);
|
||||
|
||||
Segment segment = segments[segmentNum];
|
||||
MutableLockedEntry<K, V, MKI, KI> lock = (lockTypeType == LockType.WRITE_LOCK)
|
||||
MutableLockedEntry<K, V, MKI, KI> lock = (lockType == LockType.WRITE_LOCK)
|
||||
? segment.writeLock()
|
||||
: segment.readLock();
|
||||
|
||||
V v = segment.acquireWithoutLock(copies, metaKeyInterop, keyInterop, key, value,
|
||||
segmentHash, lockTypeType == LockType.WRITE_LOCK);
|
||||
V v = segment.acquireWithoutLock(copies, metaKeyInterop, keyInterop, key, usingValue,
|
||||
segmentHash, lockType == LockType.WRITE_LOCK);
|
||||
|
||||
checkReallyUsingValue(usingValue, mustReuseValue, v);
|
||||
|
||||
lock.copies = copies;
|
||||
lock.metaKeyInterop = metaKeyInterop;
|
||||
lock.keyInterop = keyInterop;
|
||||
lock.segmentHash = segmentHash;
|
||||
|
||||
if (!isNativeValueClass && lockTypeType == LockType.WRITE_LOCK && v == null)
|
||||
v = value;
|
||||
if (!isNativeValueClass && lockType == LockType.WRITE_LOCK && v == null)
|
||||
v = usingValue;
|
||||
|
||||
lock.value(v);
|
||||
lock.key(key);
|
||||
@@ -431,6 +432,12 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
|
||||
}
|
||||
|
||||
void checkReallyUsingValue(V usingValue, boolean mustReuseValue, V returnedValue) {
|
||||
if (mustReuseValue && returnedValue != null && returnedValue != usingValue)
|
||||
throw new IllegalArgumentException("acquireUsingLocked/getUsingLocked MUST reuse given " +
|
||||
"values. Given value" + usingValue + " cannot be reused to read " + returnedValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(final Object k) {
|
||||
checkKey(k);
|
||||
@@ -849,17 +856,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
|
||||
}
|
||||
|
||||
V acquireWithoutLock(ThreadLocalCopies copies, MKI metaKeyInterop, KI keyInterop, K key, V usingValue,
|
||||
long hash2, boolean create) {
|
||||
V v = acquire0(copies, metaKeyInterop, keyInterop, key, usingValue, hash2, false);
|
||||
if (create && v == null)
|
||||
v = acquire0(copies, metaKeyInterop, keyInterop, key, usingValue, hash2, true);
|
||||
return v;
|
||||
}
|
||||
|
||||
V acquire0(ThreadLocalCopies copies, MKI metaKeyInterop, KI keyInterop, K key, V usingValue,
|
||||
long hash2, boolean create) {
|
||||
|
||||
|
||||
long keySize = metaKeyInterop.size(keyInterop, key);
|
||||
MultiStoreBytes entry = acquireTmpBytes();
|
||||
long offset = searchKey(keyInterop, metaKeyInterop, key, keySize, hash2, entry,
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 Higher Frequency Trading
|
||||
*
|
||||
* http://www.higherfrequencytrading.com
|
||||
*
|
||||
* 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 net.openhft.chronicle.map.utils;
|
||||
|
||||
import net.openhft.lang.collection.DirectBitSet;
|
||||
|
||||
/**
|
||||
* This is only used to store keys and positions, but it could store int/int key/values for another purpose.
|
||||
*/
|
||||
interface IntIntMultiMap {
|
||||
|
||||
/**
|
||||
* @return gets all the active positions as a bitset
|
||||
*/
|
||||
DirectBitSet getPositions();
|
||||
|
||||
/**
|
||||
* Add an entry. Allow duplicate hashes, but not key/position pairs.
|
||||
*
|
||||
* @param key to add
|
||||
* @param value to add
|
||||
*/
|
||||
void put(int key, int value);
|
||||
|
||||
/**
|
||||
* Remove a key/value pair.
|
||||
*
|
||||
* @param key to remove
|
||||
* @param value to remove
|
||||
* @return whether a match was found.
|
||||
*/
|
||||
boolean remove(int key, int value);
|
||||
|
||||
boolean replace(int key, int oldValue, int newValue);
|
||||
|
||||
/**
|
||||
* Used for start a search for a given key
|
||||
*
|
||||
* @return normalized key value, better to be used in subsequent calls
|
||||
*/
|
||||
int startSearch(int key);
|
||||
|
||||
int getSearchHash();
|
||||
|
||||
/**
|
||||
* Used for getting the next position for a given key
|
||||
*
|
||||
* @return the next position for the last search or negative value
|
||||
*/
|
||||
int nextPos();
|
||||
|
||||
void removePrevPos();
|
||||
|
||||
void replacePrevPos(int newValue);
|
||||
|
||||
void putAfterFailedSearch(int value);
|
||||
|
||||
void clear();
|
||||
|
||||
void forEach(EntryConsumer action);
|
||||
|
||||
static interface EntryConsumer {
|
||||
void accept(int key, int value);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -576,7 +576,7 @@ public class ChronicleMapTest {
|
||||
InstantiationException, InterruptedException, ExecutionException {
|
||||
// int runs = Integer.getInteger("runs", 10);
|
||||
int procs = Runtime.getRuntime().availableProcessors();
|
||||
int threads = procs * 2; // runs > 100 ? procs / 2 : procs;
|
||||
int threads = procs * 3;
|
||||
ExecutorService es = Executors.newFixedThreadPool(procs);
|
||||
for (int runs : new int[]{10, 50, 250, 500, 1000, 2500}) {
|
||||
// JAVA 8 produces more garbage than previous versions for internal work.
|
||||
@@ -585,6 +585,7 @@ public class ChronicleMapTest {
|
||||
OffHeapUpdatableChronicleMapBuilder<CharSequence, LongValue> builder = OffHeapUpdatableChronicleMapBuilder
|
||||
.of(CharSequence.class, LongValue.class)
|
||||
.entries(entries)
|
||||
.actualSegments(8 * 1024)
|
||||
.entryAndValueAlignment(OF_8_BYTES)
|
||||
.entrySize(24);
|
||||
|
||||
@@ -1362,7 +1363,6 @@ public class ChronicleMapTest {
|
||||
|
||||
// check that the entry was removed
|
||||
try (ReadContext<?, LongValue> context = map.getUsingLocked("one", value)) {
|
||||
assert value == context.value();
|
||||
assertEquals(false, context.present());
|
||||
assertEquals(null, context.value());
|
||||
}
|
||||
@@ -1389,12 +1389,13 @@ public class ChronicleMapTest {
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = IllegalAccessException.class)
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testAcquireUsingLockedWithString() throws IOException {
|
||||
|
||||
ChronicleMapBuilder<CharSequence, String> builder = ChronicleMapBuilder
|
||||
.of(CharSequence.class, String.class)
|
||||
.entries(1000)
|
||||
.defaultValue("")
|
||||
.entrySize(16);
|
||||
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ public class EntryCountMapTest {
|
||||
throws IOException {
|
||||
OffHeapUpdatableChronicleMapBuilder<CharSequence, LongValue> mapBuilder = OffHeapUpdatableChronicleMapBuilder.of(CharSequence.class, LongValue.class)
|
||||
.entries(entries)
|
||||
.minSegments(segments)
|
||||
.actualSegments(segments)
|
||||
.entrySize(entrySize)
|
||||
.entryAndValueAlignment(alignment);
|
||||
return mapBuilder.createPersistedTo(getPersistenceFile());
|
||||
@@ -103,33 +103,33 @@ public class EntryCountMapTest {
|
||||
testEntriesMaxSize(s, 2000, 3100, i);
|
||||
testEntriesMaxSize(s, 4000, 5100, i);
|
||||
testEntriesMaxSize(s, 5000, 7100, i);
|
||||
testEntriesMaxSize(s, 8000, 10200, i);
|
||||
testEntriesMaxSize(s, 12000, 16000, i);
|
||||
testEntriesMaxSize(s, 16000, 21300, i);
|
||||
testEntriesMaxSize(s, 8000, 11000, i);
|
||||
testEntriesMaxSize(s, 12000, 17000, i);
|
||||
testEntriesMaxSize(s, 16000, 22000, i);
|
||||
s = 32;
|
||||
testEntriesMaxSize(s, 2000, 3900, i);
|
||||
testEntriesMaxSize(s, 4000, 5900, i);
|
||||
testEntriesMaxSize(s, 5000, 7900, i);
|
||||
testEntriesMaxSize(s, 8000, 12000, i);
|
||||
testEntriesMaxSize(s, 12000, 15900, i);
|
||||
testEntriesMaxSize(s, 16000, 21300, i);
|
||||
testEntriesMaxSize(s, 32000, 42200, i);
|
||||
testEntriesMaxSize(s, 12000, 17000, i);
|
||||
testEntriesMaxSize(s, 16000, 23000, i);
|
||||
testEntriesMaxSize(s, 32000, 46000, i);
|
||||
s = 64;
|
||||
testEntriesMaxSize(s, 5000, 7550, i);
|
||||
testEntriesMaxSize(s, 8000, 11400, i);
|
||||
testEntriesMaxSize(s, 12000, 16000, i);
|
||||
testEntriesMaxSize(s, 16000, 22000, i);
|
||||
testEntriesMaxSize(s, 32000, 43000, i);
|
||||
testEntriesMaxSize(s, 40000, 54000, i);
|
||||
testEntriesMaxSize(s, 12000, 18000, i);
|
||||
testEntriesMaxSize(s, 16000, 24000, i);
|
||||
testEntriesMaxSize(s, 32000, 47000, i);
|
||||
testEntriesMaxSize(s, 40000, 60000, i);
|
||||
s = 128;
|
||||
testEntriesMaxSize(s, 16000, 23000, i);
|
||||
testEntriesMaxSize(s, 32000, 44200, i);
|
||||
testEntriesMaxSize(s, 40000, 56000, i);
|
||||
testEntriesMaxSize(s, 64000, 92000, i);
|
||||
testEntriesMaxSize(s, 129000, 190000, i);
|
||||
testEntriesMaxSize(s, 16000, 24000, i);
|
||||
testEntriesMaxSize(s, 32000, 50000, i);
|
||||
testEntriesMaxSize(s, 40000, 62000, i);
|
||||
testEntriesMaxSize(s, 64000, 98000, i);
|
||||
testEntriesMaxSize(s, 129000, 204000, i);
|
||||
s = 256;
|
||||
testEntriesMaxSize(s, 64000, 92000, i);
|
||||
testEntriesMaxSize(s, 129000, 192000, i);
|
||||
testEntriesMaxSize(s, 64000, 100000, i);
|
||||
testEntriesMaxSize(s, 129000, 213000, i);
|
||||
}
|
||||
// hyperbolic average gives more weight to small numbers.
|
||||
System.out.printf("Score: %.2f%n", scoreCount / score);
|
||||
|
||||
+7
-4
@@ -16,11 +16,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.openhft.chronicle.map.utils;
|
||||
package net.openhft.chronicle.map;
|
||||
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import net.openhft.chronicle.map.Int24Int24MultiMap;
|
||||
import net.openhft.chronicle.map.IntIntMultiMap;
|
||||
import net.openhft.chronicle.map.MultiMap;
|
||||
import net.openhft.lang.collection.SingleThreadedDirectBitSet;
|
||||
import net.openhft.lang.io.DirectStore;
|
||||
import org.junit.Assert;
|
||||
@@ -52,9 +55,9 @@ public class IntIntMultiMapTest {
|
||||
@Parameterized.Parameters
|
||||
public static Collection<Object[]> data() {
|
||||
return Arrays.asList(new Object[][]{
|
||||
{VanillaShortShortMultiMap.class},
|
||||
{VanillaI24I24MultiMap.class},
|
||||
{VanillaIntIntMultiMap.class},
|
||||
{ShortShortMultiMap.class},
|
||||
{Int24Int24MultiMap.class},
|
||||
{IntIntMultiMap.class},
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user