Tweek the sizing rules as well as optimise the hashing algo so it creates a more even distribution (for at least one set of test cases)

This commit is contained in:
Peter Lawrey
2014-10-23 21:20:46 +01:00
parent 113c51566a
commit 9a8a2512f0
8 changed files with 210 additions and 29 deletions
@@ -23,11 +23,12 @@ import sun.misc.Unsafe;
import static java.nio.ByteOrder.LITTLE_ENDIAN;
import static java.nio.ByteOrder.nativeOrder;
public final class Hasher {
public enum Hasher {
;
private static final long LONG_LEVEL_PRIME_MULTIPLE = 1011001110001111L;
private static final int SHORT_LEVEL_PRIME_MULTIPLE = 101111;
private static final long BYTE_LEVEL_PRIME_MULTIPLE = 2111;
private static final short SHORT_LEVEL_PRIME_MULTIPLE = 10191;
private static final byte BYTE_LEVEL_PRIME_MULTIPLE = 109;
private static final int INT_HASH_LOW_SHORT_MULTIPLE =
nativeOrder() == LITTLE_ENDIAN ? SHORT_LEVEL_PRIME_MULTIPLE : 1;
@@ -72,10 +73,8 @@ public final class Hasher {
}
public static long hash(long value) {
value *= 11018881818881011L;
value ^= (value >>> 41) ^ (value >>> 21);
value ^= (value >>> 41) - (value >>> 21);
value ^= (value >>> 15) + (value >>> 7);
return value;
}
private Hasher() {}
}
@@ -390,10 +390,10 @@ public class ChronicleMapBuilder<K, V> implements Cloneable,
if (actualEntriesPerSegment > 0)
return actualEntriesPerSegment;
int as = actualSegments();
long actualEntries =
as == 1 ? entries + 1 : // The extra 1 might not be needed.
as <= 4 ? entries * 10L / 9 : // 10%, assumes you are trying to be tight.
entries * 5L / 4; // 25%
long actualEntries = entries +
(as == 1 ? 1 : // The extra 1 might not be needed.
as <= 8 ? entries / 12 : // 6%, (3% was min for tests)
entries / 6); // 16% (8% was min for tests)
// round up to the next multiple of 64.
return (int) (Math.max(1, actualEntries / as) + 63) & ~63;
@@ -262,7 +262,7 @@ class ReplicatedChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
int segmentNum = getSegment(hash);
int segmentHash = segmentHash(hash);
return segment(segmentNum).acquire(copies, metaKeyWriter, keyWriter, key, value,
segmentHash, create, timeProvider.currentTimeMillis());
segmentHash, create, create ? timeProvider.currentTimeMillis() : 0L);
}
@Override
@@ -17,9 +17,9 @@
package net.openhft.chronicle.map;
import net.openhft.chronicle.common.ChronicleHashErrorListener;
import net.openhft.chronicle.common.serialization.*;
import net.openhft.chronicle.common.threadlocal.Provider;
import net.openhft.chronicle.common.threadlocal.ThreadLocalCopies;
import net.openhft.chronicle.common.serialization.*;
import net.openhft.lang.Maths;
import net.openhft.lang.collection.DirectBitSet;
import net.openhft.lang.collection.SingleThreadedDirectBitSet;
@@ -910,10 +910,10 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
ret = (int) freeList.setNextNContinuousClearBits(0, blocks);
if (ret == DirectBitSet.NOT_FOUND) {
if (blocks == 1) {
throw new IllegalArgumentException(
throw new IllegalStateException(
"Segment is full, no free entries found");
} else {
throw new IllegalArgumentException(
throw new IllegalStateException(
"Segment is full or has no ranges of " + blocks
+ " continuous free blocks"
);
@@ -970,7 +970,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
/**
* Removes a key (or key-value pair) from the Segment.
*
* <p/>
* <p>The entry will only be removed if {@code expectedValue} equals to {@code null}
* or the value previously corresponding to the specified key.
*
@@ -1446,4 +1446,11 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
return super.setValue(value);
}
}
int[] segmentSizes() {
int[] sizes = new int[segments.length];
for (int i = 0; i < segments.length; i++)
sizes[i] = segments[i].getSize();
return sizes;
}
}