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();