mirror of
https://github.com/OpenHFT/Chronicle-Map.git
synced 2021-09-13 01:26:12 +00:00
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
This commit is contained in:
@@ -318,10 +318,11 @@ public interface ChronicleHashBuilder<K, H extends ChronicleHash<K, ?, ?, ?>,
|
||||
* attacker might do this), this segment is obviously going to be tiered.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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<K, H extends ChronicleHash<K, ?, ?, ?>,
|
||||
* <p>This is a <a href="#low-level-config">low-level configuration</a>.
|
||||
*
|
||||
* @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<K, H extends ChronicleHash<K, ?, ?, ?>,
|
||||
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.
|
||||
|
||||
@@ -30,7 +30,7 @@ public interface ChronicleHashBuilderPrivateAPI<K> {
|
||||
|
||||
long entriesPerSegment();
|
||||
|
||||
long actualChunksPerSegment();
|
||||
long actualChunksPerSegmentTier();
|
||||
|
||||
int segmentHeaderSize();
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -96,7 +96,7 @@ public abstract class VanillaChronicleHash<K,
|
||||
|
||||
public long chunkSize;
|
||||
public int maxChunksPerEntry;
|
||||
public long actualChunksPerSegment;
|
||||
public long actualChunksPerSegmentTier;
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// Precomputed offsets and sizes for fast Context init
|
||||
@@ -180,12 +180,12 @@ public abstract class VanillaChronicleHash<K,
|
||||
|
||||
chunkSize = privateAPI.chunkSize();
|
||||
maxChunksPerEntry = privateAPI.maxChunksPerEntry();
|
||||
actualChunksPerSegment = privateAPI.actualChunksPerSegment();
|
||||
actualChunksPerSegmentTier = privateAPI.actualChunksPerSegmentTier();
|
||||
|
||||
// Precomputed offsets and sizes for fast Context init
|
||||
segmentHeaderSize = privateAPI.segmentHeaderSize();
|
||||
|
||||
tierHashLookupValueBits = valueBits(actualChunksPerSegment);
|
||||
tierHashLookupValueBits = valueBits(actualChunksPerSegmentTier);
|
||||
tierHashLookupKeyBits = keyBits(entriesPerSegment, tierHashLookupValueBits);
|
||||
tierHashLookupEntrySize =
|
||||
entrySize(tierHashLookupKeyBits, tierHashLookupValueBits);
|
||||
@@ -198,10 +198,10 @@ public abstract class VanillaChronicleHash<K,
|
||||
tierHashLookupOuterSize = CACHE_LINES.align(tierHashLookupInnerSize, BYTES);
|
||||
|
||||
tierFreeListInnerSize = LONGS.align(
|
||||
BYTES.alignAndConvert(actualChunksPerSegment, BITS), BYTES);
|
||||
BYTES.alignAndConvert(actualChunksPerSegmentTier, BITS), BYTES);
|
||||
tierFreeListOuterSize = CACHE_LINES.align(tierFreeListInnerSize, BYTES);
|
||||
|
||||
tierEntrySpaceInnerSize = chunkSize * actualChunksPerSegment;
|
||||
tierEntrySpaceInnerSize = chunkSize * actualChunksPerSegmentTier;
|
||||
tierEntrySpaceInnerOffset = privateAPI.segmentEntrySpaceInnerOffset();
|
||||
tierEntrySpaceOuterSize = CACHE_LINES.align(
|
||||
tierEntrySpaceInnerOffset + tierEntrySpaceInnerSize, BYTES);
|
||||
@@ -247,7 +247,7 @@ public abstract class VanillaChronicleHash<K,
|
||||
|
||||
chunkSize = wireIn.read(() -> "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<K,
|
||||
|
||||
wireOut.write(() -> "chunkSize").int64(chunkSize);
|
||||
wireOut.write(() -> "maxChunksPerEntry").int32(maxChunksPerEntry);
|
||||
wireOut.write(() -> "actualChunksPerSegment").int64(actualChunksPerSegment);
|
||||
wireOut.write(() -> "actualChunksPerSegmentTier").int64(actualChunksPerSegmentTier);
|
||||
|
||||
wireOut.write(() -> "segmentHeaderSize").int32(segmentHeaderSize);
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -143,7 +143,7 @@ public final class ChronicleMapBuilder<K, V> 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<K, V> 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<K, V> 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<K, V> actualChunksPerSegment(long actualChunksPerSegment) {
|
||||
if (actualChunksPerSegment <= 0)
|
||||
throw new IllegalArgumentException("Actual chunks per segment should be positive, " +
|
||||
actualChunksPerSegment + " given");
|
||||
this.actualChunksPerSegment = actualChunksPerSegment;
|
||||
public ChronicleMapBuilder<K, V> 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<K, V> 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<K, V> 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<K, V> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -53,8 +53,8 @@ class ChronicleMapBuilderPrivateAPI<K> implements ChronicleHashBuilderPrivateAPI
|
||||
}
|
||||
|
||||
@Override
|
||||
public long actualChunksPerSegment() {
|
||||
return b.actualChunksPerSegment();
|
||||
public long actualChunksPerSegmentTier() {
|
||||
return b.actualChunksPerSegmentTier();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -204,7 +204,7 @@ public class ReplicatedChronicleMap<K, V, R> extends VanillaChronicleMap<K, V, R
|
||||
private long bitsPerSegmentInModIterBitSet() {
|
||||
// min 128 * 8 to prevent false sharing on updating bits from different segments
|
||||
// TODO this doesn't prevent false sharing. There should be GAPS between per-segment bits
|
||||
return Maths.nextPower2(actualChunksPerSegment, 128L * 8L);
|
||||
return Maths.nextPower2(actualChunksPerSegmentTier, 128L * 8L);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -165,19 +165,14 @@ public abstract class MapEntryStages<K, V> extends HashEntryStages<K>
|
||||
"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:
|
||||
|
||||
@@ -46,9 +46,7 @@ public abstract class QueryMapEntryStages<K, V> extends MapEntryStages<K, V> {
|
||||
} 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) {
|
||||
|
||||
@@ -93,8 +93,8 @@ public final class ChronicleSetBuilder<K>
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChronicleSetBuilder<K> actualChunksPerSegment(long actualChunksPerSegment) {
|
||||
chronicleMapBuilder.actualChunksPerSegment(actualChunksPerSegment);
|
||||
public ChronicleSetBuilder<K> actualChunksPerSegmentTier(long actualChunksPerSegmentTier) {
|
||||
chronicleMapBuilder.actualChunksPerSegmentTier(actualChunksPerSegmentTier);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user