diff --git a/pom.xml b/pom.xml index b13eb35c6..c0f400ac2 100755 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ 4.0.0 chronicle-map - 3.3.1-beta-SNAPSHOT + 3.4.0-beta-SNAPSHOT OpenHFT/Chronicle-Map Chronicle-Map bundle diff --git a/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilderPrivateAPI.java b/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilderPrivateAPI.java index 6792b8b70..b4b500bcf 100644 --- a/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilderPrivateAPI.java +++ b/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilderPrivateAPI.java @@ -30,6 +30,8 @@ public interface ChronicleHashBuilderPrivateAPI { long entriesPerSegment(); + long tierHashLookupCapacity(); + long actualChunksPerSegmentTier(); int segmentHeaderSize(); diff --git a/src/main/java/net/openhft/chronicle/hash/impl/CompactOffHeapLinearHashTable.java b/src/main/java/net/openhft/chronicle/hash/impl/CompactOffHeapLinearHashTable.java index b2a50bfcc..68d4e705c 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/CompactOffHeapLinearHashTable.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/CompactOffHeapLinearHashTable.java @@ -26,7 +26,14 @@ public abstract class CompactOffHeapLinearHashTable { // to fit 64 bits per slot. public static final int MAX_TIER_CHUNKS = 1 << 30; public static final int MAX_TIER_ENTRIES = 1 << 29; - public static final double MAX_LOAD_FACTOR = .2/.3; + /** Normally, hashLookup capacity is chosen so that the lf varies from 1/3 to 2/3, at most */ + public static final double MAX_UPPER_BOUND_LOAD_FACTOR = .2/.3; + /** + * If average key/value sizes are configured wrong, and Chronicle Hash bloats up (size grows + * beyond configured .entries()), hashLookup might become too full. Stop insertion and trigger + * segment tiering, when hashLookup load factor > 0.8, because collision chains become too long. + */ + public static final double MAX_LOAD_FACTOR = 0.8; public static int valueBits(long actualChunksPerSegment) { @@ -56,12 +63,10 @@ public abstract class CompactOffHeapLinearHashTable { public static long capacityFor(long entriesPerSegment) { if (entriesPerSegment < 0L) - throw new IllegalArgumentException("entriesPerSegment should be positive"); + throw new AssertionError("entriesPerSegment should be positive"); long capacity = Maths.nextPower2(entriesPerSegment, 64L); - if (((double) entriesPerSegment) / (double) capacity > MAX_LOAD_FACTOR) { - // hash lookup shouldn't be too dense - capacity <<= 1L; - } + if (entriesPerSegment > MAX_UPPER_BOUND_LOAD_FACTOR * capacity) + capacity *= 2; return capacity; } diff --git a/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java b/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java index 0cd23670a..394dbed06 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java @@ -194,7 +194,7 @@ public abstract class VanillaChronicleHash implements return segments; } + long tierHashLookupCapacity() { + long entriesPerSegment = entriesPerSegment(); + long capacity = CompactOffHeapLinearHashTable.capacityFor(entriesPerSegment); + long maxEntriesPerTier = PoissonDistribution.inverseCumulativeProbability( + entriesPerSegment, nonTieredSegmentsPercentile); + while (maxEntriesPerTier > MAX_LOAD_FACTOR * capacity) { + capacity *= 2; + } + return capacity; + } + int segmentHeaderSize() { int segments = actualSegments(); diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilderPrivateAPI.java b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilderPrivateAPI.java index c808a246b..adb7d5575 100644 --- a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilderPrivateAPI.java +++ b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilderPrivateAPI.java @@ -52,6 +52,11 @@ class ChronicleMapBuilderPrivateAPI implements ChronicleHashBuilderPrivateAPI return b.entriesPerSegment(); } + @Override + public long tierHashLookupCapacity() { + return b.tierHashLookupCapacity(); + } + @Override public long actualChunksPerSegmentTier() { return b.actualChunksPerSegmentTier();