3939// #define TRACE_BUILDING_STATS
4040#endif
4141
42+ // A setting that can be scaled from 0 to 100 to track how the extra in memory saving from inplace indexes affects the performance
43+ // of roxie. Memory allocated = newSize + (oldSize - newSize) * inplaceSizeFactor/100;
44+ static unsigned inplaceSizeFactor = 0 ;
45+
46+ // Some settings that can be used to scale the time taken to expand nodes using the new inplace indexes.
47+ //
48+ // EstimatedTime = (TimeTakenToExpand / newPayloadSize) * oldSize * XXXSpeedFactor /100
49+ //
50+ // i.e. Estimate the time it would have taken to expand the original payload, assuming lzw compression
51+ // takes speedFactor/100 times longer than the new compression.
52+ //
53+ // This should also be used in the upcoming hybrid compression (e.g. hybrid:zstd) which uses the key compression for branches,
54+ // but the legacy compression format for leaves.
55+ //
56+ // A factor < 100 may still add time if there is a noticeable reduction in the payload data size.
57+
58+ static unsigned lz4SpeedFactor = 0 ;
59+ static unsigned zStdSpeedFactor = 0 ;
60+ static bool adjustExpansionTime = false ;
61+
62+ void setIndexScaling (unsigned _inplaceSizeFactor, unsigned _lz4SpeedFactor, unsigned _zStdSpeedFactor)
63+ {
64+ inplaceSizeFactor = _inplaceSizeFactor;
65+ lz4SpeedFactor = _lz4SpeedFactor;
66+ zStdSpeedFactor = _zStdSpeedFactor;
67+ adjustExpansionTime = (lz4SpeedFactor != 0 ) || (zStdSpeedFactor != 0 );
68+ }
69+
70+ // ---------------------------------------------------------------------------------------------------------------------
71+
4272static constexpr size32_t minRepeatCount = 2 ; // minimum number of times a 0x00 or 0x20 is repeated to generate a special opcode
4373static constexpr size32_t minRepeatXCount = 3 ; // minimum number of times a byte is repeated to generate a special opcode
4474static constexpr size32_t maxQuotedCount = 31 + 256 ; // maximum number of characters that can be quoted in a row
@@ -1665,6 +1695,8 @@ int CJHInplaceTreeNode::locateGT(const char * search, unsigned minIndex) const
16651695}
16661696
16671697
1698+ constexpr static bool traceInplaceLoadStats = false ;
1699+
16681700void CJHInplaceTreeNode::load (CKeyHdr *_keyHdr, const void *rawData, offset_t _fpos, bool needCopy)
16691701{
16701702 CJHSearchNode::load (_keyHdr, rawData, _fpos, needCopy);
@@ -1675,20 +1707,29 @@ void CJHInplaceTreeNode::load(CKeyHdr *_keyHdr, const void *rawData, offset_t _f
16751707
16761708 const byte * nullRow = nullptr ; // MORE: This should be implemented
16771709 unsigned numKeys = hdr.numKeys ;
1710+ unsigned originalKeyedSize = keyCompareLen * numKeys;
16781711 if (numKeys)
16791712 {
1713+ // only time the follow code if we are going to try and match the old timing.
1714+ CCycleTimer expansionTimer (traceInplaceLoadStats || (adjustExpansionTime && isLeaf ()));
1715+
16801716 size32_t len = hdr.keyBytes ;
16811717 size32_t copyLen = len;
16821718 const byte * originalData = ((const byte *)rawData) + sizeof (hdr);
16831719 const byte * originalPayload = nullptr ;
16841720 bool keepCompressedPayload = true ;
1721+ size32_t actualKeyedSize = len;
1722+
1723+ // For branches payloadCompression will always be COMPRESS_METHOD_NONE
1724+ CompressionMethod payloadCompression = COMPRESS_METHOD_NONE;
16851725 if (isLeaf ())
16861726 {
16871727 // Always calculate payload location so we can perform a consistency check later on.
16881728 originalPayload = queryPayload (originalData);
16891729 if (originalPayload)
16901730 {
1691- CompressionMethod payloadCompression = (CompressionMethod)*originalPayload;
1731+ actualKeyedSize = originalPayload - originalData;
1732+ payloadCompression = (CompressionMethod)*originalPayload;
16921733 switch (payloadCompression)
16931734 {
16941735 case COMPRESS_METHOD_NONE:
@@ -1699,19 +1740,27 @@ void CJHInplaceTreeNode::load(CKeyHdr *_keyHdr, const void *rawData, offset_t _f
16991740 expandPayloadOnDemand = dynamicPayloadExpansion;
17001741 keepCompressedPayload = expandPayloadOnDemand;
17011742 if (!expandPayloadOnDemand)
1702- copyLen = (originalPayload - originalData) ;
1743+ copyLen = actualKeyedSize ;
17031744 break ;
17041745 }
17051746 }
17061747 else
1748+ {
1749+ // if no payload then actualKeySize is already set....
17071750 expandPayloadOnDemand = false ;
1751+ }
17081752 }
17091753
1710- const size32_t padding = 8 - 1 ; // Ensure that unsigned8 values can be read "legally"
1754+ size32_t padding = 8 - 1 ; // Ensure that unsigned8 values can be read "legally"
1755+
1756+ // Allow the memory used by the inplace indexes to be adjusted to explore the trend of the benefits
1757+ if (inplaceSizeFactor && (actualKeyedSize < originalKeyedSize))
1758+ padding += (originalKeyedSize - actualKeyedSize) * inplaceSizeFactor / 100 ;
1759+
17111760 keyBuf = (char *) allocMem (copyLen + padding);
17121761 memcpy (keyBuf, originalData, copyLen);
17131762 memset (keyBuf+copyLen, 0 , padding);
1714- expandedSize = copyLen;
1763+ expandedSize = copyLen+padding ;
17151764
17161765 /* *** If any of the following code changes queryPayload() must also be changed. ******/
17171766
@@ -1814,6 +1863,53 @@ void CJHInplaceTreeNode::load(CKeyHdr *_keyHdr, const void *rawData, offset_t _f
18141863 assertex ((data - (const byte *)keyBuf) == len);
18151864 else
18161865 assertex ((data - originalData) == len);
1866+
1867+ // Branch nodes do not use LZW compression - so this will not attempt to adjust them.
1868+ if ((adjustExpansionTime || traceInplaceLoadStats) && (payloadCompression != COMPRESS_METHOD_NONE) && !expandPayloadOnDemand)
1869+ {
1870+ __uint64 timeTakenNs = expansionTimer.elapsedNs ();
1871+ unsigned scaling = 0 ;
1872+ switch (payloadCompression)
1873+ {
1874+ case COMPRESS_METHOD_LZW:
1875+ case COMPRESS_METHOD_LZW_LITTLE_ENDIAN:
1876+ scaling = 100 ; // only adjustment will be due to not expanding the keyed portion.
1877+ break ;
1878+ case COMPRESS_METHOD_LZ4:
1879+ case COMPRESS_METHOD_LZ4HC:
1880+ case COMPRESS_METHOD_LZ4S:
1881+ case COMPRESS_METHOD_LZ4SHC:
1882+ case COMPRESS_METHOD_LZ4HC3:
1883+ scaling = lz4SpeedFactor;
1884+ break ;
1885+ case COMPRESS_METHOD_ZSTDS:
1886+ scaling = zStdSpeedFactor;
1887+ break ;
1888+ }
1889+
1890+ if (scaling)
1891+ {
1892+ size32_t actualSizeExpanded = expandedSize - padding - actualKeyedSize; // How much data was expanded in the payload
1893+ size32_t originalSizeExpanded = actualSizeExpanded + originalKeyedSize; // How much data would there have been if the whole node was compressed?
1894+
1895+ if (actualSizeExpanded)
1896+ {
1897+ // This scaling will not work sensibly if keys are not expanded on demand.
1898+ // expected = (TimeTakenToExpand / newPayloadSize) * oldSize * XXXSpeedFactor /100;
1899+ // rearrange so the divisions happen last
1900+ __uint64 expectedTime = (timeTakenNs * originalSizeExpanded * scaling) / actualSizeExpanded / 100 ;
1901+
1902+ // Sanity check to avoid pathological times caused by context switches etc.
1903+ if (expectedTime > 500'000 )
1904+ expectedTime = 500'000 ;
1905+
1906+ if (traceInplaceLoadStats)
1907+ DBGLOG (" InplaceLoad: %s originalSize(%u) actualSize(%u), expectedTime(%llu), actualTime(%llu)" , translateFromCompMethod (payloadCompression), originalSizeExpanded, actualSizeExpanded, expectedTime, timeTakenNs);
1908+ else if (expectedTime > timeTakenNs)
1909+ NanoSleep (expectedTime- timeTakenNs);
1910+ }
1911+ }
1912+ }
18171913 }
18181914}
18191915
0 commit comments