From 2f58bfb399b2049c6d0dcb12eadf329d78d2cd8f Mon Sep 17 00:00:00 2001 From: leventov Date: Mon, 7 Dec 2015 07:20:47 +0300 Subject: [PATCH] Replace nextPosToSearchFrom with lowestPossiblyFreeChunk semantically, that allows to avoid searching free chunks though a full tier, when segment is tiered, again and again. Also, make chunk block reallocation in innerDefaultReplaceValue() and putValueDeletedEntry() check it doesn't overflow actualChunksPerSegmentTier --- .../chronicle/hash/ChronicleHashBuilder.java | 21 ++--- .../hash/ChronicleHashBuilderPrivateAPI.java | 2 +- .../chronicle/hash/impl/BigSegmentHeader.java | 14 +-- .../impl/CompactOffHeapLinearHashTable.java | 4 +- .../chronicle/hash/impl/SegmentHeader.java | 4 +- .../chronicle/hash/impl/TierCountersArea.java | 22 ++--- .../hash/impl/VanillaChronicleHash.java | 14 +-- .../hash/impl/stage/entry/SegmentStages.java | 86 +++++++++---------- .../chronicle/map/ChronicleMapBuilder.java | 69 ++++++++------- .../map/ChronicleMapBuilderPrivateAPI.java | 4 +- .../chronicle/map/ReplicatedChronicleMap.java | 2 +- .../map/impl/stage/entry/MapEntryStages.java | 9 +- .../impl/stage/query/QueryMapEntryStages.java | 4 +- .../chronicle/set/ChronicleSetBuilder.java | 4 +- .../chronicle/map/CHMUseCasesTest.java | 4 +- 15 files changed, 125 insertions(+), 138 deletions(-) diff --git a/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilder.java b/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilder.java index 0b8a7e01a..bebcae75e 100755 --- a/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilder.java +++ b/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilder.java @@ -318,10 +318,11 @@ public interface ChronicleHashBuilder, * attacker might do this), this segment is obviously going to be tiered. * *

This configuration affects the actual number of segments, if {@link #entries(long)} and - * {@link #entriesPerSegment(long)} or {@link #actualChunksPerSegment(long)} are configured. It - * affects the actual number of entries/chunks per segment, if {@link #entries(long)} and - * {@link #actualSegments(int)} are configured. If all 4 configurations, mentioned in this - * paragraph, are specified, {@code nonTieredSegmentsPercentile} is irrelevant. + * {@link #entriesPerSegment(long)} or {@link #actualChunksPerSegmentTier(long)} are configured. + * It affects the actual number of entries per segment/chunks per segment tier, if {@link + * #entries(long)} and {@link #actualSegments(int)} are configured. If all 4 configurations, + * mentioned in this paragraph, are specified, {@code nonTieredSegmentsPercentile} doesn't make + * any effect. * *

Default value is 0.99999, i. e. if hash code distribution of the keys is good, only one * segment of 100K is tiered on average. If your segment size is small and you want to improve @@ -344,7 +345,7 @@ public interface ChronicleHashBuilder, *

This is a low-level configuration. * * @param entriesPerSegment the actual maximum number entries per segment in the - * hash containers, created by this builder + * hash containers, created by this builder * @return this builder back * @see #entries(long) * @see #actualSegments(int) @@ -352,17 +353,17 @@ public interface ChronicleHashBuilder, B entriesPerSegment(long entriesPerSegment); /** - * Configures the actual number of chunks, that will be reserved for any single segment of the - * hash containers, created by this builder. This configuration is a lower-level version of + * Configures the actual number of chunks, that will be reserved for any single segment tier of + * the hash containers, created by this builder. This configuration is a lower-level version of * {@link #entriesPerSegment(long)}. Makes sense only if {@link #actualChunkSize(int)}, * {@link #actualSegments(int)} and {@link #entriesPerSegment(long)} are also configured * manually. * - * @param actualChunksPerSegment the actual number of segments, reserved per segment in the - * hash containers, created by this builder + * @param actualChunksPerSegmentTier the actual number of chunks, reserved per segment tier in + * the hash containers, created by this builder * @return this builder back */ - B actualChunksPerSegment(long actualChunksPerSegment); + B actualChunksPerSegmentTier(long actualChunksPerSegmentTier); /** * Configures the actual number of segments in the hash containers, created by this builder. diff --git a/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilderPrivateAPI.java b/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilderPrivateAPI.java index a0dc481cf..6792b8b70 100644 --- a/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilderPrivateAPI.java +++ b/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilderPrivateAPI.java @@ -30,7 +30,7 @@ public interface ChronicleHashBuilderPrivateAPI { long entriesPerSegment(); - long actualChunksPerSegment(); + long actualChunksPerSegmentTier(); int segmentHeaderSize(); diff --git a/src/main/java/net/openhft/chronicle/hash/impl/BigSegmentHeader.java b/src/main/java/net/openhft/chronicle/hash/impl/BigSegmentHeader.java index b99e57833..ef77e535a 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/BigSegmentHeader.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/BigSegmentHeader.java @@ -50,10 +50,11 @@ public final class BigSegmentHeader implements SegmentHeader { static final int WAIT_PARTY = 1; static final long SIZE_OFFSET = LOCK_OFFSET + 8L; // 32-bit - static final long NEXT_POS_TO_SEARCH_FROM_OFFSET = SIZE_OFFSET + 4L; + static final long LOWEST_POSSIBLY_FREE_CHUNK_OFFSET = ENTRIES_OFFSET + 4L; - static final long EXCLUSIVE_LOCK_HOLDER_THREAD_ID_OFFSET = NEXT_POS_TO_SEARCH_FROM_OFFSET + 4L; + static final long EXCLUSIVE_LOCK_HOLDER_THREAD_ID_OFFSET = + LOWEST_POSSIBLY_FREE_CHUNK_OFFSET + 4L; static final long DELETED_OFFSET = EXCLUSIVE_LOCK_HOLDER_THREAD_ID_OFFSET + 8L; @@ -89,13 +90,14 @@ public final class BigSegmentHeader implements SegmentHeader { } @Override - public long nextPosToSearchFrom(long address) { - return OS.memory().readInt(address + NEXT_POS_TO_SEARCH_FROM_OFFSET) & UNSIGNED_INT_MASK; + public long lowestPossiblyFreeChunk(long address) { + return OS.memory().readInt(address + LOWEST_POSSIBLY_FREE_CHUNK_OFFSET) & UNSIGNED_INT_MASK; } @Override - public void nextPosToSearchFrom(long address, long nextPosToSearchFrom) { - OS.memory().writeInt(address + NEXT_POS_TO_SEARCH_FROM_OFFSET, (int) nextPosToSearchFrom); + public void lowestPossiblyFreeChunk(long address, long lowestPossiblyFreeChunk) { + OS.memory().writeInt(address + LOWEST_POSSIBLY_FREE_CHUNK_OFFSET, + (int) lowestPossiblyFreeChunk); } private static long getLockWord(long address) { 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 5660c9aa6..88e52a532 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/CompactOffHeapLinearHashTable.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/CompactOffHeapLinearHashTable.java @@ -24,8 +24,8 @@ import static net.openhft.chronicle.algo.MemoryUnit.BYTES; public abstract class CompactOffHeapLinearHashTable { // to fit 64 bits per slot. - public static final int MAX_SEGMENT_CHUNKS = 1 << 30; - public static final int MAX_SEGMENT_ENTRIES = 1 << 29; + public static final int MAX_TIER_CHUNKS = 1 << 30; + public static final int MAX_TIER_ENTRIES = 1 << 29; public static int valueBits(long actualChunksPerSegment) { diff --git a/src/main/java/net/openhft/chronicle/hash/impl/SegmentHeader.java b/src/main/java/net/openhft/chronicle/hash/impl/SegmentHeader.java index 2af155876..d9876b725 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/SegmentHeader.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/SegmentHeader.java @@ -25,8 +25,8 @@ public interface SegmentHeader { long deleted(long address); void deleted(long address, long deleted); - long nextPosToSearchFrom(long address); - void nextPosToSearchFrom(long address, long nextPosToSearchFrom); + long lowestPossiblyFreeChunk(long address); + void lowestPossiblyFreeChunk(long address, long lowestPossiblyFreeChunk); void readLock(long address); void readLockInterruptibly(long address); diff --git a/src/main/java/net/openhft/chronicle/hash/impl/TierCountersArea.java b/src/main/java/net/openhft/chronicle/hash/impl/TierCountersArea.java index b59c612d8..cee3666f0 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/TierCountersArea.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/TierCountersArea.java @@ -20,10 +20,10 @@ import net.openhft.chronicle.core.Memory; import net.openhft.chronicle.core.OS; /** - * The reason why this is not a data value generated class, and offsets are allocated by - * hand -- this functionality is accessed concurrently from many threads, and data value generated - * classes are stateful - so need to keep an instance in thread local that seems to be overall more - * pain than gain. + * The reason why this is not implemented via value interface, and offsets are allocated by hand -- + * this functionality is accessed concurrently from many threads, and native value implementations + * are stateful - so need to keep an instance in thread local that seems to be overall more pain + * than gain. */ public enum TierCountersArea { ; @@ -31,8 +31,8 @@ public enum TierCountersArea { private static Memory memory = OS.memory(); public static final long NEXT_TIER_INDEX_OFFSET = 0L; public static final long PREV_TIER_INDEX_OFFSET = NEXT_TIER_INDEX_OFFSET + 8L; - public static final long NEXT_POS_TO_SEARCH_FROM_TIERED_OFFSET = PREV_TIER_INDEX_OFFSET + 8L; - public static final long SEGMENT_INDEX_OFFSET = NEXT_POS_TO_SEARCH_FROM_TIERED_OFFSET + 8L; + public static final long LOWEST_POSSIBLY_FREE_CHUNK_TIERED_OFFSET = PREV_TIER_INDEX_OFFSET + 8L; + public static final long SEGMENT_INDEX_OFFSET = LOWEST_POSSIBLY_FREE_CHUNK_TIERED_OFFSET + 8L; public static final long TIER_OFFSET = SEGMENT_INDEX_OFFSET + 4L; public static long nextTierIndex(long address) { @@ -43,13 +43,13 @@ public enum TierCountersArea { memory.writeLong(address + NEXT_TIER_INDEX_OFFSET, nextTierIndex); } - public static long nextPosToSearchFromTiered(long address) { - return memory.readLong(address + NEXT_POS_TO_SEARCH_FROM_TIERED_OFFSET); + public static long lowestPossiblyFreeChunkTiered(long address) { + return memory.readLong(address + LOWEST_POSSIBLY_FREE_CHUNK_TIERED_OFFSET); } - public static void nextPosToSearchFromTiered(long address, long nextPosToSearchFrom) { - memory.writeLong(address + NEXT_POS_TO_SEARCH_FROM_TIERED_OFFSET, - nextPosToSearchFrom); + public static void lowestPossiblyFreeChunkTiered(long address, long lowestPossiblyFreeChunk) { + memory.writeLong(address + LOWEST_POSSIBLY_FREE_CHUNK_TIERED_OFFSET, + lowestPossiblyFreeChunk); } public static long prevTierIndex(long address) { 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 2fd7fb0be..6d49c4278 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java @@ -96,7 +96,7 @@ public abstract class VanillaChronicleHash "chunkSize").int64(); maxChunksPerEntry = wireIn.read(() -> "maxChunksPerEntry").int32(); - actualChunksPerSegment = wireIn.read(() -> "actualChunksPerSegment").int64(); + actualChunksPerSegmentTier = wireIn.read(() -> "actualChunksPerSegmentTier").int64(); segmentHeaderSize = wireIn.read(() -> "segmentHeaderSize").int32(); @@ -292,7 +292,7 @@ public abstract class VanillaChronicleHash "chunkSize").int64(chunkSize); wireOut.write(() -> "maxChunksPerEntry").int32(maxChunksPerEntry); - wireOut.write(() -> "actualChunksPerSegment").int64(actualChunksPerSegment); + wireOut.write(() -> "actualChunksPerSegmentTier").int64(actualChunksPerSegmentTier); wireOut.write(() -> "segmentHeaderSize").int32(segmentHeaderSize); diff --git a/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/SegmentStages.java b/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/SegmentStages.java index 5dc351874..246846312 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/SegmentStages.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/SegmentStages.java @@ -16,7 +16,6 @@ package net.openhft.chronicle.hash.impl.stage.entry; -import net.openhft.chronicle.algo.bitset.BitSetFrame; import net.openhft.chronicle.algo.bitset.ReusableBitSet; import net.openhft.chronicle.algo.bitset.SingleThreadedFlatBitSetFrame; import net.openhft.chronicle.algo.bytes.Access; @@ -40,6 +39,7 @@ import java.util.Objects; import static net.openhft.chronicle.algo.MemoryUnit.BITS; import static net.openhft.chronicle.algo.MemoryUnit.LONGS; +import static net.openhft.chronicle.algo.bitset.BitSetFrame.NOT_FOUND; import static net.openhft.chronicle.hash.impl.LocalLockState.UNLOCKED; import static net.openhft.chronicle.hash.impl.VanillaChronicleHash.TIER_COUNTERS_AREA_SIZE; @@ -74,19 +74,20 @@ public abstract class SegmentStages implements SegmentLock, LocksInterface { segmentHeader.size(segmentHeaderAddress, size); } - long nextPosToSearchFrom() { + long lowestPossiblyFreeChunk() { if (tier == 0) { - return segmentHeader.nextPosToSearchFrom(segmentHeaderAddress); + return segmentHeader.lowestPossiblyFreeChunk(segmentHeaderAddress); } else { - return nextPosToSearchFromTiered(); + return TierCountersArea.lowestPossiblyFreeChunkTiered(tierCountersAreaAddr()); } } - public void nextPosToSearchFrom(long nextPosToSearchFrom) { + public void lowestPossiblyFreeChunk(long lowestPossiblyFreeChunk) { if (tier == 0) { - segmentHeader.nextPosToSearchFrom(segmentHeaderAddress, nextPosToSearchFrom); + segmentHeader.lowestPossiblyFreeChunk(segmentHeaderAddress, lowestPossiblyFreeChunk); } else { - nextPosToSearchFromTiered(nextPosToSearchFrom); + TierCountersArea.lowestPossiblyFreeChunkTiered(tierCountersAreaAddr(), + lowestPossiblyFreeChunk); } } @@ -519,14 +520,6 @@ public abstract class SegmentStages implements SegmentLock, LocksInterface { TierCountersArea.nextTierIndex(tierCountersAreaAddr(), nextTierIndex); } - public long nextPosToSearchFromTiered() { - return TierCountersArea.nextPosToSearchFromTiered(tierCountersAreaAddr()); - } - - public void nextPosToSearchFromTiered(long nextPosToSearchFrom) { - TierCountersArea.nextPosToSearchFromTiered(tierCountersAreaAddr(), nextPosToSearchFrom); - } - public long prevTierIndex() { return TierCountersArea.prevTierIndex(tierCountersAreaAddr()); } @@ -573,8 +566,8 @@ public abstract class SegmentStages implements SegmentLock, LocksInterface { @Stage("Segment") public final PointerBytesStore segmentBS = new PointerBytesStore(); @Stage("Segment") public final Bytes segmentBytes = new VanillaBytes(segmentBS); - @Stage("Segment") public final ReusableBitSet freeList = new ReusableBitSet( - new SingleThreadedFlatBitSetFrame(LONGS.align(hh.h().actualChunksPerSegment, BITS)), + @Stage("Segment") private final ReusableBitSet freeList = new ReusableBitSet( + new SingleThreadedFlatBitSetFrame(LONGS.align(hh.h().actualChunksPerSegmentTier, BITS)), Access.nativeAccess(), null, 0); @Stage("Segment") long entrySpaceOffset = 0; @@ -612,50 +605,51 @@ public abstract class SegmentStages implements SegmentLock, LocksInterface { entrySpaceOffset = 0; } + @Stage("Segment") public long allocReturnCode(int chunks) { VanillaChronicleHash h = hh.h(); if (chunks > h.maxChunksPerEntry) { throw new IllegalArgumentException("Entry is too large: requires " + chunks + " chucks, " + h.maxChunksPerEntry + " is maximum."); } - long ret = freeList.setNextNContinuousClearBits(nextPosToSearchFrom(), chunks); - if (ret == BitSetFrame.NOT_FOUND || ret + chunks > h.actualChunksPerSegment) { - if (ret != BitSetFrame.NOT_FOUND && - ret + chunks > h.actualChunksPerSegment && ret < h.actualChunksPerSegment) { - freeList.clearRange(ret, h.actualChunksPerSegment); + long lowestPossiblyFreeChunk = lowestPossiblyFreeChunk(); + if (lowestPossiblyFreeChunk == h.actualChunksPerSegmentTier) + return -1; + assert lowestPossiblyFreeChunk < h.actualChunksPerSegmentTier; + long ret = freeList.setNextNContinuousClearBits(lowestPossiblyFreeChunk, chunks); + if (ret == NOT_FOUND || ret + chunks > h.actualChunksPerSegmentTier) { + if (ret + chunks > h.actualChunksPerSegmentTier) { + assert ret != NOT_FOUND; + freeList.clearRange(ret, ret + chunks); } - ret = freeList.setNextNContinuousClearBits(0L, chunks); - if (ret == BitSetFrame.NOT_FOUND || ret + chunks > h.actualChunksPerSegment) { - if (ret != BitSetFrame.NOT_FOUND && - ret + chunks > h.actualChunksPerSegment && - ret < h.actualChunksPerSegment) { - freeList.clearRange(ret, h.actualChunksPerSegment); - } - return -1; - } - updateNextPosToSearchFrom(ret, chunks); + return -1; } else { - // if bit at nextPosToSearchFrom is clear, it was skipped because - // more than 1 chunk was requested. Don't move nextPosToSearchFrom + // if bit at lowestPossiblyFreeChunk is clear, it was skipped because + // more than 1 chunk was requested. Don't move lowestPossiblyFreeChunk // in this case. chunks == 1 clause is just a fast path. - if (chunks == 1 || freeList.isSet(nextPosToSearchFrom())) { - updateNextPosToSearchFrom(ret, chunks); + if (chunks == 1 || freeList.isSet(lowestPossiblyFreeChunk)) { + lowestPossiblyFreeChunk(ret + chunks); } + return ret; } - return ret; } + @Stage("Segment") + public boolean realloc(long fromPos, int oldChunks, int newChunks) { + if (fromPos + newChunks < hh.h().actualChunksPerSegmentTier && + freeList.isRangeClear(fromPos + oldChunks, fromPos + newChunks)) { + freeList.setRange(fromPos + oldChunks, fromPos + newChunks); + return true; + } else { + return false; + } + } + + @Stage("Segment") public void free(long fromPos, int chunks) { freeList.clearRange(fromPos, fromPos + chunks); - if (fromPos < nextPosToSearchFrom()) - nextPosToSearchFrom(fromPos); - } - - public void updateNextPosToSearchFrom(long allocated, int chunks) { - long nextPosToSearchFrom = allocated + chunks; - if (nextPosToSearchFrom >= hh.h().actualChunksPerSegment) - nextPosToSearchFrom = 0L; - nextPosToSearchFrom(nextPosToSearchFrom); + if (fromPos < lowestPossiblyFreeChunk()) + lowestPossiblyFreeChunk(fromPos); } public void verifyTierCountersAreaData() { diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java index 707ccd97d..ebc483b04 100755 --- a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java @@ -143,7 +143,7 @@ public final class ChronicleMapBuilder implements private int actualSegments = -1; // used when reading the number of entries per private long entriesPerSegment = -1L; - private long actualChunksPerSegment = -1L; + private long actualChunksPerSegmentTier = -1L; private double averageKeySize = UNDEFINED_DOUBLE_CONFIG; private K averageKey; private K sampleKey; @@ -675,8 +675,8 @@ public final class ChronicleMapBuilder implements int maxChunksPerEntry() { if (constantlySizedEntries()) return 1; - long actualChunksPerSegment = actualChunksPerSegment(); - int result = (int) Math.min(actualChunksPerSegment, (long) Integer.MAX_VALUE); + long actualChunksPerSegmentTier = actualChunksPerSegmentTier(); + int result = (int) Math.min(actualChunksPerSegmentTier, (long) Integer.MAX_VALUE); if (this.maxChunksPerEntry > 0) result = Math.min(this.maxChunksPerEntry, result); return result; @@ -767,60 +767,59 @@ public final class ChronicleMapBuilder implements entriesPerSegment = PoissonDistribution.inverseCumulativeProbability( averageEntriesPerSegment, nonTieredSegmentsPercentile); } - boolean actualChunksDefined = actualChunksPerSegment > 0; + boolean actualChunksDefined = actualChunksPerSegmentTier > 0; if (!actualChunksDefined) { double averageChunksPerEntry = averageChunksPerEntry(); if (entriesPerSegment * averageChunksPerEntry > - MAX_SEGMENT_CHUNKS) - throw new IllegalStateException("Max chunks per segment is " + - MAX_SEGMENT_CHUNKS + - " configured entries() and " + - "actualSegments() so that there should be " + entriesPerSegment + - " entries per segment, while average chunks per entry is " + - averageChunksPerEntry); + MAX_TIER_CHUNKS) + throw new IllegalStateException("Max chunks per segment tier is " + + MAX_TIER_CHUNKS + " configured entries() and actualSegments() so that " + + "there should be " + entriesPerSegment + " entries per segment tier, " + + "while average chunks per entry is " + averageChunksPerEntry); } - if (entriesPerSegment > MAX_SEGMENT_ENTRIES) + if (entriesPerSegment > MAX_TIER_ENTRIES) throw new IllegalStateException("shouldn't be more than " + - MAX_SEGMENT_ENTRIES + " entries per segment"); + MAX_TIER_ENTRIES + " entries per segment"); return entriesPerSegment; } @Override - public ChronicleMapBuilder actualChunksPerSegment(long actualChunksPerSegment) { - if (actualChunksPerSegment <= 0) - throw new IllegalArgumentException("Actual chunks per segment should be positive, " + - actualChunksPerSegment + " given"); - this.actualChunksPerSegment = actualChunksPerSegment; + public ChronicleMapBuilder actualChunksPerSegmentTier(long actualChunksPerSegmentTier) { + if (actualChunksPerSegmentTier <= 0 || actualChunksPerSegmentTier > MAX_TIER_CHUNKS) + throw new IllegalArgumentException("Actual chunks per segment tier should be in [1, " + + MAX_TIER_CHUNKS + "], range, " + actualChunksPerSegmentTier + " given"); + + this.actualChunksPerSegmentTier = actualChunksPerSegmentTier; return this; } - private void checkActualChunksPerSegmentIsConfiguredOnlyIfOtherLowLevelConfigsAreManual() { - if (actualChunksPerSegment > 0) { + private void checkActualChunksPerSegmentTierIsConfiguredOnlyIfOtherLowLevelConfigsAreManual() { + if (actualChunksPerSegmentTier > 0) { if (entriesPerSegment <= 0 || (actualChunkSize <= 0 && !constantlySizedEntries()) || actualSegments <= 0) - throw new IllegalStateException("Actual chunks per segment could be configured " + - "only if other three low level configs are manual: " + + throw new IllegalStateException("Actual chunks per segment tier could be " + + "configured only if other three low level configs are manual: " + "entriesPerSegment(), actualSegments() and actualChunkSize(), unless " + "both keys and value sizes are constant"); } } private void checkActualChunksPerSegmentGreaterOrEqualToEntries() { - if (actualChunksPerSegment > 0 && entriesPerSegment > 0 && - entriesPerSegment > actualChunksPerSegment) { + if (actualChunksPerSegmentTier > 0 && entriesPerSegment > 0 && + entriesPerSegment > actualChunksPerSegmentTier) { throw new IllegalStateException("Entries per segment couldn't be greater than " + - "actual chunks per segment. Entries: " + entriesPerSegment + ", " + - "chunks: " + actualChunksPerSegment + " is configured"); + "actual chunks per segment tier. Entries: " + entriesPerSegment + ", " + + "chunks: " + actualChunksPerSegmentTier + " is configured"); } } - long actualChunksPerSegment() { - if (actualChunksPerSegment > 0) - return actualChunksPerSegment; - return chunksPerSegment(entriesPerSegment()); + long actualChunksPerSegmentTier() { + if (actualChunksPerSegmentTier > 0) + return actualChunksPerSegmentTier; + return chunksPerSegmentTier(entriesPerSegment()); } - private long chunksPerSegment(long entriesPerSegment) { + private long chunksPerSegmentTier(long entriesPerSegment) { return round(entriesPerSegment * averageChunksPerEntry()); } @@ -949,7 +948,7 @@ public final class ChronicleMapBuilder implements } private int hashLookupSlotBytes(long entriesPerSegment) { - int valueBits = valueBits(chunksPerSegment(entriesPerSegment)); + int valueBits = valueBits(chunksPerSegmentTier(entriesPerSegment)); int keyBits = keyBits(entriesPerSegment, valueBits); return entrySize(keyBits, valueBits); } @@ -1089,7 +1088,7 @@ public final class ChronicleMapBuilder implements ", actualSegments=" + pretty(actualSegments) + ", minSegments=" + pretty(minSegments) + ", entriesPerSegment=" + pretty(entriesPerSegment) + - ", actualChunksPerSegment=" + pretty(actualChunksPerSegment) + + ", actualChunksPerSegmentTier=" + pretty(actualChunksPerSegmentTier) + ", averageKeySize=" + pretty(averageKeySize) + ", sampleKeyForConstantSizeComputation=" + pretty(sampleKey) + ", averageValueSize=" + pretty(averageValueSize) + @@ -1523,12 +1522,12 @@ public final class ChronicleMapBuilder implements } private void stateChecks() { - checkActualChunksPerSegmentIsConfiguredOnlyIfOtherLowLevelConfigsAreManual(); + checkActualChunksPerSegmentTierIsConfiguredOnlyIfOtherLowLevelConfigsAreManual(); checkActualChunksPerSegmentGreaterOrEqualToEntries(); } private boolean allLowLevelConfigurationsAreManual() { - return actualSegments > 0 && entriesPerSegment > 0 && actualChunksPerSegment > 0 + return actualSegments > 0 && entriesPerSegment > 0 && actualChunksPerSegmentTier > 0 && actualChunkSize > 0; } diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilderPrivateAPI.java b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilderPrivateAPI.java index 93daacabb..c808a246b 100644 --- a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilderPrivateAPI.java +++ b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilderPrivateAPI.java @@ -53,8 +53,8 @@ class ChronicleMapBuilderPrivateAPI implements ChronicleHashBuilderPrivateAPI } @Override - public long actualChunksPerSegment() { - return b.actualChunksPerSegment(); + public long actualChunksPerSegmentTier() { + return b.actualChunksPerSegmentTier(); } @Override diff --git a/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java b/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java index 6d71a5776..65abcd1bb 100644 --- a/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java @@ -204,7 +204,7 @@ public class ReplicatedChronicleMap extends VanillaChronicleMap extends HashEntryStages "entry takes " + newSizeInChunks + " chunks, " + m.maxChunksPerEntry + " is maximum."); } - if (s.freeList.isRangeClear(pos + entrySizeInChunks, pos + newSizeInChunks)) { - s.freeList.setRange(pos + entrySizeInChunks, pos + newSizeInChunks); + if (s.realloc(pos, entrySizeInChunks, newSizeInChunks)) { break newValueDoesNotFit; } relocation(newValue, newSizeOfEverythingBeforeValue); return; } else if (newSizeInChunks < entrySizeInChunks) { // Freeing extra chunks - s.freeList.clearRange(pos + newSizeInChunks, pos + entrySizeInChunks); - // Do NOT reset nextPosToSearchFrom, because if value - // once was larger it could easily became larger again, - // But if these chunks will be taken by that time, - // this entry will need to be relocated. + s.free(pos + newSizeInChunks, entrySizeInChunks - newSizeInChunks); } // new size != old size => size is not constant => size is actually written => // to prevent (at least) this execution: diff --git a/src/main/java/net/openhft/chronicle/map/impl/stage/query/QueryMapEntryStages.java b/src/main/java/net/openhft/chronicle/map/impl/stage/query/QueryMapEntryStages.java index ab3f276b5..f6b416c55 100644 --- a/src/main/java/net/openhft/chronicle/map/impl/stage/query/QueryMapEntryStages.java +++ b/src/main/java/net/openhft/chronicle/map/impl/stage/query/QueryMapEntryStages.java @@ -46,9 +46,7 @@ public abstract class QueryMapEntryStages extends MapEntryStages { } else { newSizeInChunks = entrySizeInChunks; } - if (pos + newSizeInChunks < s.freeList.logicalSize() && - s.freeList.isRangeClear(pos, pos + newSizeInChunks)) { - s.freeList.setRange(pos, pos + newSizeInChunks); + if (s.realloc(pos, 0, newSizeInChunks)) { s.innerWriteLock.lock(); allocatedChunks.incrementSegmentEntriesIfNeeded(); if (newValueSizeIsDifferent) { diff --git a/src/main/java/net/openhft/chronicle/set/ChronicleSetBuilder.java b/src/main/java/net/openhft/chronicle/set/ChronicleSetBuilder.java index 226ba3349..a90d97fc5 100755 --- a/src/main/java/net/openhft/chronicle/set/ChronicleSetBuilder.java +++ b/src/main/java/net/openhft/chronicle/set/ChronicleSetBuilder.java @@ -93,8 +93,8 @@ public final class ChronicleSetBuilder } @Override - public ChronicleSetBuilder actualChunksPerSegment(long actualChunksPerSegment) { - chronicleMapBuilder.actualChunksPerSegment(actualChunksPerSegment); + public ChronicleSetBuilder actualChunksPerSegmentTier(long actualChunksPerSegmentTier) { + chronicleMapBuilder.actualChunksPerSegmentTier(actualChunksPerSegmentTier); return this; } diff --git a/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java b/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java index e03db5e8c..85c51ab3d 100644 --- a/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java +++ b/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java @@ -33,11 +33,9 @@ import net.openhft.chronicle.values.Array; import net.openhft.chronicle.values.MaxUtf8Length; import net.openhft.chronicle.values.Range; import net.openhft.chronicle.values.Values; -import net.openhft.lang.values.DoubleArray; import org.jetbrains.annotations.NotNull; import org.junit.After; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -216,7 +214,7 @@ public class CHMUseCasesTest { map1.valueClass()) .entriesPerSegment(vanillaMap.entriesPerSegment) .actualSegments(vanillaMap.actualSegments) - .actualChunksPerSegment(vanillaMap.actualChunksPerSegment); + .actualChunksPerSegmentTier(vanillaMap.actualChunksPerSegmentTier); if (!vanillaMap.constantlySizedEntry) { builder.actualChunkSize((int) vanillaMap.chunkSize); builder.worstAlignment(vanillaMap.worstAlignment);