From 7afe1eca130624fbf41ffafe1d9bf3ef702babcd Mon Sep 17 00:00:00 2001 From: leventov Date: Sun, 17 Jul 2016 23:11:22 +0700 Subject: [PATCH] Optimize VanillaChronicleMap.get() and getUsing() --- .../chronicle/map/ReplicatedChronicleMap.java | 10 ++ .../chronicle/map/VanillaChronicleMap.java | 168 +++++++++++++++++- 2 files changed, 174 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java b/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java index f56b431a0..dd41c5d1b 100644 --- a/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java @@ -946,4 +946,14 @@ public class ReplicatedChronicleMap extends VanillaChronicleMap public transient MapEntryOperations entryOperations; public transient MapMethods methods; + private transient boolean defaultEntryOperationsAndMethods; public transient DefaultValueProvider defaultValueProvider; transient ThreadLocal cxt; @@ -132,9 +136,11 @@ public class VanillaChronicleMap putReturnsNull = builder.putReturnsNull(); removeReturnsNull = builder.removeReturnsNull(); - this.entryOperations = (MapEntryOperations) builder.entryOperations; - this.methods = (MapMethods) builder.methods; - this.defaultValueProvider = builder.defaultValueProvider; + entryOperations = (MapEntryOperations) builder.entryOperations; + methods = (MapMethods) builder.methods; + defaultEntryOperationsAndMethods = entryOperations == DefaultSpi.mapEntryOperations() && + methods == DefaultSpi.mapMethods(); + defaultValueProvider = builder.defaultValueProvider; } @Override @@ -309,14 +315,168 @@ public class VanillaChronicleMap @Override public V get(Object key) { + return defaultEntryOperationsAndMethods ? optimizedGet(key, null) : defaultGet(key); + } + + final V defaultGet(Object key) { try (QueryContextInterface q = queryContext(key)) { methods.get(q, q.defaultReturnValue()); return q.defaultReturnValue().returnValue(); } } + private V optimizedGet(Object key, V using) { + checkKey(key); + CompiledMapQueryContext q = (CompiledMapQueryContext) mapContext(); + Data inputKey = q.inputKeyDataAccess().getData((K) key); + try { + Throwable primaryExc = null; + long inputKeySize = inputKey.size(); + + long keyHash = inputKey.hash(LongHashFunction.xx_r39()); + HashSplitting hs = this.hashSplitting; + int segmentIndex = hs.segmentIndex(keyHash); + long segmentHeaderAddress = segmentHeaderAddress(segmentIndex); + boolean needReadUnlock = false; + try { + CompactOffHeapLinearHashTable hl = this.hashLookup; + long searchKey = hl.maskUnsetKey(hs.segmentHash(keyHash)); + long searchStartPos = hl.hlPos(searchKey); + boolean needReadLock = true; + initLocks: + { + int indexOfThisContext = q.indexInContextChain; + for (int i = 0, size = q.contextChain.size(); i < size; i++) { + if (i == indexOfThisContext) + continue; + LocksInterface c = ((LocksInterface) (q.contextChain.get(i))); + if (c.segmentHeaderInit() && + c.segmentHeaderAddress() == segmentHeaderAddress && + c.locksInit()) { + LocksInterface root = c.rootContextLockedOnThisSegment(); + if (root.totalReadLockCount() > 0 || root.totalUpdateLockCount() > 0 || + root.totalWriteLockCount() > 0) { + needReadLock = false; + break initLocks; + } + } + } + } + if (needReadLock) { + BigSegmentHeader.INSTANCE.readLock(segmentHeaderAddress); + needReadUnlock = true; + } + return tieredValue(q, segmentHeaderAddress, segmentIndex, searchKey, searchStartPos, + inputKeySize, inputKey, using); + } catch (Throwable t) { + primaryExc = t; + throw t; + } finally { + if (primaryExc != null) { + try { + getClose(q, segmentHeaderAddress, needReadUnlock); + } catch (Throwable suppressedExc) { + primaryExc.addSuppressed(suppressedExc); + } + } else { + getClose(q, segmentHeaderAddress, needReadUnlock); + } + } + } finally { + q.doCloseInputKeyDataAccess(); + } + } + + private void getClose(CompiledMapQueryContext q, long segmentHeaderAddress, + boolean needReadUnlock) { + if (needReadUnlock) + BigSegmentHeader.INSTANCE.readUnlock(segmentHeaderAddress); + q.doCloseUsed(); + } + + private V tieredValue(CompiledMapQueryContext q, + long segmentHeaderAddress, int segmentIndex, + long searchKey, long searchStartPos, + long inputKeySize, Data inputKey, V using) { + int tier = 0; + long tierBaseAddr = segmentBaseAddr(segmentIndex); + while (true) { + V value = searchValue(q, searchKey, searchStartPos, tierBaseAddr, + inputKeySize, inputKey, using); + if (value != null) + return value; + long nextTierIndex; + if (tier == 0) { + nextTierIndex = BigSegmentHeader.INSTANCE.nextTierIndex(segmentHeaderAddress); + } else { + nextTierIndex = TierCountersArea.nextTierIndex( + tierBaseAddr + tierHashLookupOuterSize); + } + if (nextTierIndex == 0) + return null; + tier++; + tierBaseAddr = tierIndexToBaseAddr(nextTierIndex); + + } + } + + private V searchValue(CompiledMapQueryContext q, + long searchKey, long searchStartPos, long tierBaseAddr, + long inputKeySize, Data inputKey, V using) { + CompactOffHeapLinearHashTable hl = this.hashLookup; + + PointerBytesStore segmentBytesStore = q.segmentBS; + segmentBytesStore.set(tierBaseAddr, tierSize); + Bytes bs = q.segmentBytes; + bs.clear(); + long freeListOffset = tierHashLookupOuterSize + TIER_COUNTERS_AREA_SIZE; + long entrySpaceOffset = freeListOffset + tierFreeListOuterSize + tierEntrySpaceInnerOffset; + + long hlPos = searchStartPos; + searchLoop: + while (true) { + long entryPos; + nextPos: { + while (true) { + long entry = hl.readEntryVolatile(tierBaseAddr, hlPos); + if (hl.empty(entry)) { + break searchLoop; + } + hlPos = hl.step(hlPos); + if (hlPos == searchStartPos) + throw new IllegalStateException("HashLookup overflow should never occur"); + + if ((hl.key(entry)) == searchKey) { + entryPos = hl.value(entry); + break nextPos; + } + } + } + + long keySizeOffset = entrySpaceOffset + (entryPos * chunkSize); + bs.readLimit(bs.capacity()); + bs.readPosition(keySizeOffset); + long keySize = keySizeMarshaller.readSize(bs); + long keyOffset = bs.readPosition(); + if (!((inputKeySize == keySize) && + (inputKey.equivalent(segmentBytesStore, keyOffset)))) { + continue; + } + long valueSizeOffset = keyOffset + keySize; + bs.readPosition(valueSizeOffset); + long valueSize = readValueSize(bs); + return q.valueReader.read(bs, valueSize, using); + } + return null; + } + @Override public V getUsing(K key, V usingValue) { + return defaultEntryOperationsAndMethods ? optimizedGet(key, usingValue) : + defaultGetUsing(key, usingValue); + } + + final V defaultGetUsing(K key, V usingValue) { try (QueryContextInterface q = queryContext(key)) { q.usingReturnValue().initUsingReturnValue(usingValue); methods.get(q, q.usingReturnValue());