diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java index c8afdf9e0..81d845abf 100755 --- a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java @@ -382,7 +382,17 @@ public final class ChronicleMapBuilder implements Cloneable, return this; } - double averageEntrySize(boolean replicated) { + static class EntrySizeInfo { + final double averageEntrySize; + final int worstAlignment; + + public EntrySizeInfo(double averageEntrySize, int worstAlignment) { + this.averageEntrySize = averageEntrySize; + this.worstAlignment = worstAlignment; + } + } + + private EntrySizeInfo entrySizeInfo(boolean replicated) { double size = metaDataBytes; double keySize = averageKeySize(); size += averageSizeEncodingSize(keyBuilder, keySize); @@ -392,39 +402,42 @@ public final class ChronicleMapBuilder implements Cloneable, double valueSize = averageValueSize(); size += averageSizeEncodingSize(valueBuilder, valueSize); Alignment alignment = valueAlignment(); + int worstAlignment; if (alignment != Alignment.NO_ALIGNMENT) { if (constantlySizedKeys() && valueBuilder.constantSizeEncodingSizeMarshaller()) { long constantSizeBeforeAlignment = round(size); if (constantlySizedValues()) { // see specialEntrySpaceOffset() long totalDataSize = constantSizeBeforeAlignment + constantValueSize(); - size += alignment.alignAddr(totalDataSize) - totalDataSize; + worstAlignment = (int) (alignment.alignAddr(totalDataSize) - totalDataSize); } else { + determineAlignment: if (actualChunkSize > 0) { - size += averageAlignmentAssumingChunkSize(constantSizeBeforeAlignment, + worstAlignment = worstAlignmentAssumingChunkSize(constantSizeBeforeAlignment, actualChunkSize); } else { - for (int chunkSize : new int[]{8, 4}) { - double expectedAverageAlignment = averageAlignmentAssumingChunkSize( - constantSizeBeforeAlignment, chunkSize); - if (size + expectedAverageAlignment + valueSize >= - maxDefaultChunksPerAverageEntry(replicated) * chunkSize || - chunkSize == 4) { - size += expectedAverageAlignment; - break; - } + int chunkSize = 8; + worstAlignment = worstAlignmentAssumingChunkSize( + constantSizeBeforeAlignment, chunkSize); + if (size + worstAlignment + valueSize >= + maxDefaultChunksPerAverageEntry(replicated) * chunkSize) { + break determineAlignment; } + chunkSize = 4; + worstAlignment = worstAlignmentAssumingChunkSize( + constantSizeBeforeAlignment, chunkSize); } } } else { - if (alignment.alignment() > 0) { - // assume worst case, we always lose most possible bytes for alignment - size += alignment.alignment() - 1; - } + // assume worst case, we always lose most possible bytes for alignment + worstAlignment = alignment.alignment() - 1; } + } else { + worstAlignment = 0; } + size += worstAlignment; size += valueSize; - return size; + return new EntrySizeInfo(size, worstAlignment); } /** @@ -459,20 +472,27 @@ public final class ChronicleMapBuilder implements Cloneable, return lower * (upper - averageSize) + upper * (averageSize - lower); } - private double averageAlignmentAssumingChunkSize( + private int worstAlignmentAssumingChunkSize( long constantSizeBeforeAlignment, int chunkSize) { if (valueAlignment() == Alignment.NO_ALIGNMENT) - return 0.0; + return 0; Alignment valueAlignment = valueAlignment(); long firstAlignment = valueAlignment.alignAddr(constantSizeBeforeAlignment) - constantSizeBeforeAlignment; int alignment = valueAlignment.alignment(); int gcdOfAlignmentAndChunkSize = greatestCommonDivisor(alignment, chunkSize); if (gcdOfAlignmentAndChunkSize == alignment) - return firstAlignment; + return (int) firstAlignment; // assume worst by now because we cannot predict alignment in VanillaCM.entrySize() method // before allocation - return alignment - gcdOfAlignmentAndChunkSize; + long worstAlignment = firstAlignment; + while (worstAlignment + gcdOfAlignmentAndChunkSize < alignment) + worstAlignment += gcdOfAlignmentAndChunkSize; + return (int) worstAlignment; + } + + int worstAlignment(boolean replicated) { + return entrySizeInfo(replicated).worstAlignment; } static int greatestCommonDivisor(int a, int b) { @@ -483,7 +503,7 @@ public final class ChronicleMapBuilder implements Cloneable, long chunkSize(boolean replicated) { if (actualChunkSize > 0) return actualChunkSize; - double averageEntrySize = averageEntrySize(replicated); + double averageEntrySize = entrySizeInfo(replicated).averageEntrySize; if (constantlySizedEntries()) return round(averageEntrySize); int maxChunkSize = 1 << 30; @@ -505,7 +525,7 @@ public final class ChronicleMapBuilder implements Cloneable, // assuming we always has worst internal fragmentation. This affects total segment // entry space which is allocated lazily on Linux (main target platform) // so we can afford this - return (averageEntrySize(replicated) + chunkSize - 1) / chunkSize; + return (entrySizeInfo(replicated).averageEntrySize + chunkSize - 1) / chunkSize; } private static int maxDefaultChunksPerAverageEntry(boolean replicated) { diff --git a/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java b/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java index 64ee58d25..86756d749 100644 --- a/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java @@ -783,6 +783,7 @@ final class ReplicatedChronicleMap, final Alignment alignment; final boolean constantlySizedEntry; final int worstAlignment; + final boolean couldNotDetermineAlignmentBeforeAllocation; final int specialEntrySpaceOffset; final int actualSegments; final long actualChunksPerSegment; @@ -154,12 +155,10 @@ class VanillaChronicleMap, this.chunkSize = builder.chunkSize(replicated); this.alignment = builder.valueAlignment(); this.constantlySizedEntry = builder.constantlySizedEntries(); + this.worstAlignment = builder.worstAlignment(replicated); int alignment = this.alignment.alignment(); - if (this.alignment != Alignment.NO_ALIGNMENT) { - this.worstAlignment = alignment - greatestCommonDivisor((int) chunkSize, alignment); - } else { - this.worstAlignment = 0; - } + this.couldNotDetermineAlignmentBeforeAllocation = + greatestCommonDivisor((int) chunkSize, alignment) != alignment; this.specialEntrySpaceOffset = builder.specialEntrySpaceOffset(replicated); this.errorListener = builder.errorListener(); @@ -1616,7 +1615,7 @@ class VanillaChronicleMap, valueSizeMarshaller.sizeEncodingSize(valueSize); if (constantlySizedEntry) { return alignment.alignAddr(sizeOfEverythingBeforeValue + valueSize); - } else if (worstAlignment > 0) { + } else if (couldNotDetermineAlignmentBeforeAllocation) { return sizeOfEverythingBeforeValue + worstAlignment + valueSize; } else { return alignment.alignAddr(sizeOfEverythingBeforeValue) + valueSize; @@ -1673,6 +1672,7 @@ class VanillaChronicleMap, continue; // key is found entry.skip(keySize); + segmentState.pos = pos; // for WriteLocked.close() return readValueAndNotifyGet(copies, key, keySize, toKey, readValue, usingValue, toValue, entry); }