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:
leventov
2015-12-07 07:20:47 +03:00
parent 82988e7435
commit 2f58bfb399
15 changed files with 125 additions and 138 deletions
@@ -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;
}