From ead85dfd7c9c566f585396bb88f2eef0347fbfc6 Mon Sep 17 00:00:00 2001 From: Max Neunhoeffer Date: Sun, 17 May 2015 11:32:58 -0700 Subject: [PATCH] Use multiple buckets in AssocMulti hash. --- lib/Basics/AssocMulti.h | 580 ++++++++++++++++++++++------------------ 1 file changed, 325 insertions(+), 255 deletions(-) diff --git a/lib/Basics/AssocMulti.h b/lib/Basics/AssocMulti.h index 45c35a03be..35b14f616f 100644 --- a/lib/Basics/AssocMulti.h +++ b/lib/Basics/AssocMulti.h @@ -121,12 +121,23 @@ namespace triagens { // list of all items with the same key }; - IndexType _nrAlloc; // the size of the table - IndexType _nrUsed; // the number of used entries - IndexType _nrCollisions; // the number of entries that have - // a key that was previously in the table + struct Bucket { + IndexType _nrAlloc; // the size of the table + IndexType _nrUsed; // the number of used entries + IndexType _nrCollisions; // the number of entries that have + // a key that was previously in the table - Entry* _table; // the table itself + Entry* _table; // the table itself + + Bucket () : _nrAlloc(0), _nrUsed(0), _nrCollisions(0), + _table(nullptr) { + } + // Intentionally no destructor, the AssocMulti class takes + // care of freeing the tables! + }; + + std::vector _buckets; + size_t _bucketsMask; #ifdef TRI_INTERNAL_STATS uint64_t _nrFinds; // statistics: number of lookup calls @@ -162,11 +173,8 @@ namespace triagens { IsEqualKeyElementFuncType isEqualKeyElement, IsEqualElementElementFuncType isEqualElementElement, IsEqualElementElementFuncType isEqualElementElementByKey, - IndexType initialSize = 64) - : _nrAlloc(initialSize), - _nrUsed(0), - _nrCollisions(0), - _table(nullptr), + size_t numberBuckets = 1, + IndexType initialSize = 64) : #ifdef TRI_INTERNAL_STATS _nrFinds(0), _nrAdds(0), _nrRems(0), _nrResizes(0), _nrProbes(0), _nrProbesF(0), _nrProbesD(0), @@ -177,16 +185,36 @@ namespace triagens { _isEqualElementElement(isEqualElementElement), _isEqualElementElementByKey(isEqualElementElementByKey) { + // Make the number of buckets a power of two: + size_t ex = 0; + size_t nr = 1; + numberBuckets >>= 1; + while (numberBuckets > 0) { + ex += 1; + numberBuckets >>= 1; + nr <<= 1; + } + numberBuckets = nr; + _bucketsMask = nr - 1; + + std::cout << "FUXX: numberBuckets=" << numberBuckets + << " _bucketsMask=" << _bucketsMask << std::endl; try { - _table = new Entry[_nrAlloc]; - IndexType i; - for (i = 0; i < _nrAlloc; i++) { - invalidateEntry(i); + for (size_t j = 0; j < numberBuckets; j++) { + _buckets.emplace_back(); + Bucket& b = _buckets.back(); + b._nrAlloc = initialSize; + b._table = new Entry[b._nrAlloc]; + for (IndexType i = 0; i < b._nrAlloc; i++) { + invalidateEntry(b, i); + } } } catch (...) { - _table = nullptr; - _nrAlloc = 0; + for (auto& b : _buckets) { + b._table = nullptr; + b._nrAlloc = 0; + } throw; } } @@ -196,9 +224,11 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// ~AssocMulti () { - if (_table != nullptr) { - delete [] _table; - _table = nullptr; + for (auto& b : _buckets) { + if (b._table != nullptr) { + delete [] b._table; + b._table = nullptr; + } } } @@ -212,7 +242,14 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// size_t memoryUsage () const { - return static_cast (_nrAlloc * sizeof(Entry)); + size_t res = 0; + size_t count = 0; + for (auto& b : _buckets) { + res += static_cast (b._nrAlloc) * sizeof(Entry); + std::cout << "Bucket: " << count++ << " _nrAlloc=" << b._nrAlloc + << " _nrUsed=" << b._nrUsed << std::endl; + } + return res; } //////////////////////////////////////////////////////////////////////////////// @@ -220,7 +257,11 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// size_t size () const { - return static_cast(_nrUsed); + size_t res = 0; + for (auto& b : _buckets) { + res += static_cast(b._nrUsed); + } + return res; } //////////////////////////////////////////////////////////////////////////////// @@ -228,7 +269,11 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// size_t capacity () const { - return static_cast(_nrAlloc); + size_t res = 0; + for (auto& b : _buckets) { + res += static_cast(b._nrAlloc); + } + return res; } //////////////////////////////////////////////////////////////////////////////// @@ -236,8 +281,8 @@ namespace triagens { /// this may return a nullptr //////////////////////////////////////////////////////////////////////////////// - Element* at (size_t position) const { - return _table[position].ptr; + Element* at (Bucket& b, size_t position) const { + return b._table[position].ptr; } //////////////////////////////////////////////////////////////////////////////// @@ -259,9 +304,13 @@ namespace triagens { check(true, true); #endif + // compute the hash by the key only first + uint64_t hashByKey = _hashElement(element, true); + Bucket& b = _buckets[hashByKey & _bucketsMask]; + // if we were adding and the table is more than 2/3 full, extend it - if (2 * _nrAlloc < 3 * _nrUsed) { - resizeInternal(2 * _nrAlloc + 1); + if (2 * b._nrAlloc < 3 * b._nrUsed) { + resizeInternal(b, 2 * b._nrAlloc + 1); } #ifdef TRI_INTERNAL_STATS @@ -269,15 +318,13 @@ namespace triagens { _nrAdds++; #endif - // compute the hash by the key only first - uint64_t hashByKey = _hashElement(element, true); IndexType hashIndex = hashToIndex(hashByKey); - IndexType i = hashIndex % _nrAlloc; + IndexType i = hashIndex % b._nrAlloc; // If this slot is free, just use it: - if (nullptr == _table[i].ptr) { - _table[i] = { hashByKey, element, INVALID_INDEX, INVALID_INDEX }; - _nrUsed++; + if (nullptr == b._table[i].ptr) { + b._table[i] = { hashByKey, element, INVALID_INDEX, INVALID_INDEX }; + b._nrUsed++; // no collision generated here! #ifdef TRI_CHECK_MULTI_POINTER_HASH check(true, true); @@ -287,12 +334,12 @@ namespace triagens { // Now find the first slot with an entry with the same key // that is the start of a linked list, or a free slot: - while (_table[i].ptr != nullptr && - (_table[i].prev != INVALID_INDEX || - _table[i].hashCache != hashByKey || - ! _isEqualElementElementByKey(element, _table[i].ptr)) + while (b._table[i].ptr != nullptr && + (b._table[i].prev != INVALID_INDEX || + b._table[i].hashCache != hashByKey || + ! _isEqualElementElementByKey(element, b._table[i].ptr)) ) { - i = incr(i); + i = incr(b, i); #ifdef TRI_INTERNAL_STATS // update statistics _ProbesA++; @@ -301,9 +348,9 @@ namespace triagens { } // If this is free, we are the first with this key: - if (nullptr == _table[i].ptr) { - _table[i] = { hashByKey, element, INVALID_INDEX, INVALID_INDEX }; - _nrUsed++; + if (nullptr == b._table[i].ptr) { + b._table[i] = { hashByKey, element, INVALID_INDEX, INVALID_INDEX }; + b._nrUsed++; // no collision generated here either! #ifdef TRI_CHECK_MULTI_POINTER_HASH check(true, true); @@ -315,11 +362,11 @@ namespace triagens { // list of which we want to make element a member. Perhaps an // equal element is right here: if (checkEquality && - _isEqualElementElement(element, _table[i].ptr)) { - old = _table[i].ptr; + _isEqualElementElement(element, b._table[i].ptr)) { + old = b._table[i].ptr; if (overwrite) { - TRI_ASSERT(_table[i].hashCache == hashByKey); - _table[i].ptr = element; + TRI_ASSERT(b._table[i].hashCache == hashByKey); + b._table[i].ptr = element; } #ifdef TRI_CHECK_MULTI_POINTER_HASH check(true, true); @@ -329,15 +376,15 @@ namespace triagens { // Now find a new home for element in this linked list: uint64_t hashByElm; - IndexType j = findElementPlace(element, checkEquality, hashByElm); + IndexType j = findElementPlace(b, element, checkEquality, hashByElm); - old = _table[j].ptr; + old = b._table[j].ptr; // if we found an element, return if (old != nullptr) { if (overwrite) { - _table[j].hashCache = hashByElm; - _table[j].ptr = element; + b._table[j].hashCache = hashByElm; + b._table[j].ptr = element; } #ifdef TRI_CHECK_MULTI_POINTER_HASH check(true, true); @@ -346,14 +393,14 @@ namespace triagens { } // add a new element to the associative array and linked list (in pos 2): - _table[j] = { hashByElm, element, _table[i].next, i }; - _table[i].next = j; + b._table[j] = { hashByElm, element, b._table[i].next, i }; + b._table[i].next = j; // Finally, we need to find the successor to patch it up: - if (_table[j].next != INVALID_INDEX) { - _table[_table[j].next].prev = j; + if (b._table[j].next != INVALID_INDEX) { + b._table[b._table[j].next].prev = j; } - _nrUsed++; - _nrCollisions++; + b._nrUsed++; + b._nrCollisions++; #ifdef TRI_CHECK_MULTI_POINTER_HASH check(true, true); @@ -369,7 +416,7 @@ namespace triagens { private: - void insertFirst (Element* element, uint64_t hashByKey) { + void insertFirst (Bucket& b, Element* element, uint64_t hashByKey) { #ifdef TRI_CHECK_MULTI_POINTER_HASH check(true, true); @@ -381,12 +428,12 @@ namespace triagens { #endif IndexType hashIndex = hashToIndex(hashByKey); - IndexType i = hashIndex % _nrAlloc; + IndexType i = hashIndex % b._nrAlloc; // If this slot is free, just use it: - if (nullptr == _table[i].ptr) { - _table[i] = { hashByKey, element, INVALID_INDEX, INVALID_INDEX }; - _nrUsed++; + if (nullptr == b._table[i].ptr) { + b._table[i] = { hashByKey, element, INVALID_INDEX, INVALID_INDEX }; + b._nrUsed++; // no collision generated here! #ifdef TRI_CHECK_MULTI_POINTER_HASH check(true, true); @@ -395,8 +442,8 @@ namespace triagens { // Now find the first slot with an entry with the same key // that is the start of a linked list, or a free slot: - while (_table[i].ptr != nullptr) { - i = incr(i); + while (b._table[i].ptr != nullptr) { + i = incr(b, i); #ifdef TRI_INTERNAL_STATS // update statistics _ProbesA++; @@ -404,8 +451,8 @@ namespace triagens { } // We are the first with this key: - _table[i] = { hashByKey, element, INVALID_INDEX, INVALID_INDEX }; - _nrUsed++; + b._table[i] = { hashByKey, element, INVALID_INDEX, INVALID_INDEX }; + b._nrUsed++; // no collision generated here either! #ifdef TRI_CHECK_MULTI_POINTER_HASH check(true, true); @@ -419,7 +466,7 @@ namespace triagens { /// example the case when resizing. //////////////////////////////////////////////////////////////////////////////// - void insertFurther (Element* element, + void insertFurther (Bucket& b, Element* element, uint64_t hashByKey, uint64_t hashByElm) { #ifdef TRI_CHECK_MULTI_POINTER_HASH check(true, true); @@ -432,18 +479,18 @@ namespace triagens { // We need the beginning of the doubly linked list: IndexType hashIndex = hashToIndex(hashByKey); - IndexType i = hashIndex % _nrAlloc; + IndexType i = hashIndex % b._nrAlloc; - TRI_ASSERT(nullptr != _table[i].ptr); + TRI_ASSERT(nullptr != b._table[i].ptr); // Find the first slot with an entry with the same key // that is the start of a linked list, or a free slot: - while (_table[i].ptr != nullptr && - (_table[i].prev != INVALID_INDEX || - _table[i].hashCache != hashByKey || - ! _isEqualElementElementByKey(element, _table[i].ptr)) + while (b._table[i].ptr != nullptr && + (b._table[i].prev != INVALID_INDEX || + b._table[i].hashCache != hashByKey || + ! _isEqualElementElementByKey(element, b._table[i].ptr)) ) { - i = incr(i); + i = incr(b, i); #ifdef TRI_INTERNAL_STATS // update statistics _ProbesA++; @@ -452,31 +499,31 @@ namespace triagens { } // If this is free, we are the first with this key, a contradiction: - TRI_ASSERT(nullptr != _table[i].ptr); + TRI_ASSERT(nullptr != b._table[i].ptr); // Now, entry i points to the beginning of the linked // list of which we want to make element a member. // Now find a new home for element in this linked list: hashIndex = hashToIndex(hashByElm); - IndexType j = hashIndex % _nrAlloc; + IndexType j = hashIndex % b._nrAlloc; - while (_table[j].ptr != nullptr) { - j = incr(j); + while (b._table[j].ptr != nullptr) { + j = incr(b, j); #ifdef TRI_INTERNAL_STATS _nrProbes++; #endif } // add the element to the hash and linked list (in pos 2): - _table[j] = { hashByElm, element, _table[i].next, i }; - _table[i].next = j; + b._table[j] = { hashByElm, element, b._table[i].next, i }; + b._table[i].next = j; // Finally, we need to find the successor to patch it up: - if (_table[j].next != INVALID_INDEX) { - _table[_table[j].next].prev = j; + if (b._table[j].next != INVALID_INDEX) { + b._table[b._table[j].next].prev = j; } - _nrUsed++; - _nrCollisions++; + b._nrUsed++; + b._nrCollisions++; #ifdef TRI_CHECK_MULTI_POINTER_HASH check(true, true); @@ -497,8 +544,9 @@ namespace triagens { _nrFinds++; #endif - i = lookupByElement(element); - return _table[i].ptr; + Bucket* b; + i = lookupByElement(element, b); + return b->_table[i].ptr; } //////////////////////////////////////////////////////////////////////////////// @@ -512,8 +560,9 @@ namespace triagens { // compute the hash uint64_t hashByKey = _hashKey(key); + Bucket const& b = _buckets[hashByKey & _bucketsMask]; IndexType hashIndex = hashToIndex(hashByKey); - IndexType i = hashIndex % _nrAlloc; + IndexType i = hashIndex % b._nrAlloc; #ifdef TRI_INTERNAL_STATS // update statistics @@ -521,23 +570,23 @@ namespace triagens { #endif // search the table - while (_table[i].ptr != nullptr && - (_table[i].prev != INVALID_INDEX || - _table[i].hashCache != hashByKey || - ! _isEqualKeyElement(key, _table[i].ptr)) + while (b._table[i].ptr != nullptr && + (b._table[i].prev != INVALID_INDEX || + b._table[i].hashCache != hashByKey || + ! _isEqualKeyElement(key, b._table[i].ptr)) ) { - i = incr(i); + i = incr(b, i); #ifdef TRI_INTERNAL_STATS _nrProbesF++; #endif } - if (_table[i].ptr != nullptr) { + if (b._table[i].ptr != nullptr) { // We found the beginning of the linked list: do { - result->push_back(_table[i].ptr); - i = _table[i].next; + result->push_back(b._table[i].ptr); + i = b._table[i].next; } while (i != INVALID_INDEX && (limit == 0 || result->size() < limit)); @@ -559,8 +608,9 @@ namespace triagens { // compute the hash uint64_t hashByKey = _hashElement(element, true); + Bucket const& b = _buckets[hashByKey & _bucketsMask]; IndexType hashIndex = hashToIndex(hashByKey); - IndexType i = hashIndex % _nrAlloc; + IndexType i = hashIndex % b._nrAlloc; #ifdef TRI_INTERNAL_STATS // update statistics @@ -568,23 +618,23 @@ namespace triagens { #endif // search the table - while (_table[i].ptr != nullptr && - (_table[i].prev != INVALID_INDEX || - _table[i].hashCache != hashByKey || - ! _isEqualElementElementByKey(element, _table[i].ptr)) + while (b._table[i].ptr != nullptr && + (b._table[i].prev != INVALID_INDEX || + b._table[i].hashCache != hashByKey || + ! _isEqualElementElementByKey(element, b._table[i].ptr)) ) { - i = incr(i); + i = incr(b, i); #ifdef TRI_INTERNAL_STATS _nrProbesF++; #endif } - if (_table[i].ptr != nullptr) { + if (b._table[i].ptr != nullptr) { // We found the beginning of the linked list: do { - result->push_back(_table[i].ptr); - i = _table[i].next; + result->push_back(b._table[i].ptr); + i = b._table[i].next; } while (i != INVALID_INDEX && (limit == 0 || result->size() < limit)); @@ -605,20 +655,22 @@ namespace triagens { std::unique_ptr> result (new std::vector()); + uint64_t hashByKey = _hashElement(element, true); + Bucket const& b = _buckets[hashByKey & _bucketsMask]; uint64_t hashByElm; - IndexType i = findElementPlace(element, true, hashByElm); - if (_table[i].ptr == nullptr) { + IndexType i = findElementPlace(b, element, true, hashByElm); + if (b._table[i].ptr == nullptr) { return nullptr; } // compute the hash // continue search of the table while (true) { - i = _table[i].next; + i = b._table[i].next; if (i == INVALID_INDEX || (limit != 0 && result->size() >= limit)) { break; } - result->push_back(_table[i].ptr); + result->push_back(b._table[i].ptr); } // return whatever we found @@ -651,56 +703,57 @@ namespace triagens { #ifdef TRI_CHECK_MULTI_POINTER_HASH check(true, true); #endif - IndexType i = lookupByElement(element); - if (_table[i].ptr == nullptr) { + Bucket* b; + IndexType i = lookupByElement(element, b); + if (b->_table[i].ptr == nullptr) { return nullptr; } - Element* old = _table[i].ptr; + Element* old = b->_table[i].ptr; // We have to delete entry i - if (_table[i].prev == INVALID_INDEX) { + if (b->_table[i].prev == INVALID_INDEX) { // This is the first in its linked list. - j = _table[i].next; + j = b->_table[i].next; if (j == INVALID_INDEX) { // The only one in its linked list, simply remove it and heal // the hole: - invalidateEntry(i); + invalidateEntry(*b, i); #ifdef TRI_CHECK_MULTI_POINTER_HASH check(false, false); #endif - healHole(i); + healHole(*b, i); // this element did not create a collision } else { // There is at least one successor in position j. - _table[j].prev = INVALID_INDEX; - moveEntry(j, i); + b->_table[j].prev = INVALID_INDEX; + moveEntry(*b, j, i); // We need to exchange the hashCache value by that of the key: - _table[i].hashCache = _hashElement(_table[i].ptr, true); + b->_table[i].hashCache = _hashElement(b->_table[i].ptr, true); #ifdef TRI_CHECK_MULTI_POINTER_HASH check(false, false); #endif - healHole(j); - _nrCollisions--; // one collision less + healHole(*b, j); + b->_nrCollisions--; // one collision less } } else { // This one is not the first in its linked list - j = _table[i].prev; - _table[j].next = _table[i].next; - j = _table[i].next; + j = b->_table[i].prev; + b->_table[j].next = b->_table[i].next; + j = b->_table[i].next; if (j != INVALID_INDEX) { // We are not the last in the linked list. - _table[j].prev = _table[i].prev; + b->_table[j].prev = b->_table[i].prev; } - invalidateEntry(i); + invalidateEntry(*b, i); #ifdef TRI_CHECK_MULTI_POINTER_HASH check(false, false); #endif - healHole(i); - _nrCollisions--; + healHole(*b, i); + b->_nrCollisions--; } - _nrUsed--; + b->_nrUsed--; #ifdef TRI_CHECK_MULTI_POINTER_HASH check(true, true); #endif @@ -713,15 +766,18 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// int resize (IndexType size) throw() { - if (2 * (2*size+1) < 3 * _nrUsed) { - return TRI_ERROR_BAD_PARAMETER; - } + size /= _buckets.size(); + for (auto& b : _buckets) { + if (2 * (2*size+1) < 3 * b._nrUsed) { + return TRI_ERROR_BAD_PARAMETER; + } - try { - resizeInternal(2*size+1); - } - catch (...) { - return TRI_ERROR_OUT_OF_MEMORY; + try { + resizeInternal(b, 2*size+1); + } + catch (...) { + return TRI_ERROR_OUT_OF_MEMORY; + } } return TRI_ERROR_NO_ERROR; } @@ -734,8 +790,14 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// double selectivity () { - return _nrUsed > 0 ? - (_nrUsed - _nrCollisions) / _nrUsed : + size_t nrUsed = 0; + size_t nrCollisions = 0; + for (auto& b : _buckets) { + nrUsed += b._nrUsed; + nrCollisions += b._nrCollisions; + } + return nrUsed > 0 ? + (nrUsed - nrCollisions) / nrUsed : 1.0; } @@ -745,9 +807,11 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// void iterate (std::function callback) { - for (IndexType i = 0; i < _nrAlloc; i++) { - if (_table[i].ptr != nullptr) { - callback(_table[i].ptr); + for (auto& b : _buckets) { + for (IndexType i = 0; i < b._nrAlloc; i++) { + if (b._table[i].ptr != nullptr) { + callback(b._table[i].ptr); + } } } } @@ -762,39 +826,39 @@ namespace triagens { /// @brief increment IndexType by 1 modulo _nrAlloc: //////////////////////////////////////////////////////////////////////////////// - inline IndexType incr (IndexType i) const { - IndexType dummy = (++i) - _nrAlloc; - return i < _nrAlloc ? i : dummy; + inline IndexType incr (Bucket const& b, IndexType i) const { + IndexType dummy = (++i) - b._nrAlloc; + return i < b._nrAlloc ? i : dummy; } //////////////////////////////////////////////////////////////////////////////// /// @brief resize the array, internal method //////////////////////////////////////////////////////////////////////////////// - void resizeInternal (IndexType size) { + void resizeInternal (Bucket& b, IndexType size) { LOG_ACTION("edge-index-resize, target size: %llu", (unsigned long long) size); double start = TRI_microtime(); - Entry* oldTable = _table; - IndexType oldAlloc = _nrAlloc; + Entry* oldTable = b._table; + IndexType oldAlloc = b._nrAlloc; - _nrAlloc = TRI_NearPrime(size); + b._nrAlloc = TRI_NearPrime(size); try { - _table = new Entry[_nrAlloc]; + b._table = new Entry[b._nrAlloc]; IndexType i; - for (i = 0; i < _nrAlloc; i++) { - invalidateEntry(i); + for (i = 0; i < b._nrAlloc; i++) { + invalidateEntry(b, i); } } catch (...) { - _nrAlloc = oldAlloc; - _table = oldTable; + b._nrAlloc = oldAlloc; + b._table = oldTable; throw; } - _nrUsed = 0; + b._nrUsed = 0; #ifdef TRI_INTERNAL_STATS _nrResizes++; #endif @@ -805,7 +869,7 @@ namespace triagens { if (oldTable[j].ptr != nullptr && oldTable[j].prev == INVALID_INDEX) { // This is a "first" one in its doubly linked list: - insertFirst(oldTable[j].ptr, oldTable[j].hashCache); + insertFirst(b, oldTable[j].ptr, oldTable[j].hashCache); uint64_t hashByKey = oldTable[j].hashCache; // Now walk to the end of the list: IndexType k = j; @@ -814,7 +878,7 @@ namespace triagens { } // Now insert all of them backwards, not repeating k: while (k != j) { - insertFurther(oldTable[k].ptr, hashByKey, + insertFurther(b, oldTable[k].ptr, hashByKey, oldTable[k].hashCache); k = oldTable[k].prev; } @@ -836,85 +900,87 @@ namespace triagens { bool check (bool checkCount, bool checkPositions) const { std::cout << "Performing AssocMulti check " << checkCount << checkPositions << std::endl; - IndexType i, ii, j, k; + for (auto& b : _buckets) { + IndexType i, ii, j, k; - bool ok = true; - IndexType count = 0; + bool ok = true; + IndexType count = 0; - for (i = 0;i < _nrAlloc;i++) { - if (_table[i].ptr != nullptr) { - count++; - if (_table[i].prev != INVALID_INDEX) { - if (_table[_table[i].prev].next != i) { - std::cout << "Alarm prev " << i << std::endl; - ok = false; + for (i = 0;i < b._nrAlloc;i++) { + if (b._table[i].ptr != nullptr) { + count++; + if (b._table[i].prev != INVALID_INDEX) { + if (b._table[b._table[i].prev].next != i) { + std::cout << "Alarm prev " << i << std::endl; + ok = false; + } } - } - if (_table[i].next != INVALID_INDEX) { - if (_table[_table[i].next].prev != i) { - std::cout << "Alarm next " << i << std::endl; - ok = false; + if (b._table[i].next != INVALID_INDEX) { + if (b._table[b._table[i].next].prev != i) { + std::cout << "Alarm next " << i << std::endl; + ok = false; + } } - } - ii = i; - j = _table[ii].next; - while (j != INVALID_INDEX) { - if (j == i) { - std::cout << "Alarm cycle " << i << std::endl; - ok = false; - break; + ii = i; + j = b._table[ii].next; + while (j != INVALID_INDEX) { + if (j == i) { + std::cout << "Alarm cycle " << i << std::endl; + ok = false; + break; + } + ii = j; + j = b._table[ii].next; } - ii = j; - j = _table[ii].next; } } - } - if (checkCount && count != _nrUsed) { - std::cout << "Alarm _nrUsed wrong " << _nrUsed << " != " - << count << "!" << std::endl; - ok = false; - } - if (checkPositions) { - for (i = 0;i < _nrAlloc;i++) { - if (_table[i].ptr != nullptr) { - IndexType hashIndex; - if (_table[i].prev == INVALID_INDEX) { - // We are the first in a linked list. - uint64_t hashByKey = _hashElement(_table[i].ptr, true); - hashIndex = hashToIndex(hashByKey); - j = hashIndex % _nrAlloc; - if (_table[i].hashCache != hashByKey) { - std::cout << "Alarm hashCache wrong " << i << std::endl; - } - for (k = j; k != i; ) { - if (_table[k].ptr == nullptr || - (_table[k].prev == INVALID_INDEX && - _isEqualElementElementByKey(_table[i].ptr, - _table[k].ptr))) { - ok = false; - std::cout << "Alarm pos bykey: " << i << std::endl; + if (checkCount && count != b._nrUsed) { + std::cout << "Alarm _nrUsed wrong " << b._nrUsed << " != " + << count << "!" << std::endl; + ok = false; + } + if (checkPositions) { + for (i = 0;i < b._nrAlloc;i++) { + if (b._table[i].ptr != nullptr) { + IndexType hashIndex; + if (b._table[i].prev == INVALID_INDEX) { + // We are the first in a linked list. + uint64_t hashByKey = _hashElement(b._table[i].ptr, true); + hashIndex = hashToIndex(hashByKey); + j = hashIndex % b._nrAlloc; + if (b._table[i].hashCache != hashByKey) { + std::cout << "Alarm hashCache wrong " << i << std::endl; } - k = incr(k); - } - } - else { - // We are not the first in a linked list. - uint64_t hashByElm = _hashElement(_table[i].ptr, false); - hashIndex = hashToIndex(hashByElm); - j = hashIndex % _nrAlloc; - if (_table[i].hashCache != hashByElm) { - std::cout << "Alarm hashCache wrong " << i << std::endl; - } - for (k = j; k != i; ) { - if (_table[k].ptr == nullptr || - _isEqualElementElement(_table[i].ptr, - _table[k].ptr)) { - ok = false; - std::cout << "Alarm unique: " << k << ", " - << i << std::endl; + for (k = j; k != i; ) { + if (b._table[k].ptr == nullptr || + (b._table[k].prev == INVALID_INDEX && + _isEqualElementElementByKey(b._table[i].ptr, + b._table[k].ptr))) { + ok = false; + std::cout << "Alarm pos bykey: " << i << std::endl; + } + k = incr(b, k); + } + } + else { + // We are not the first in a linked list. + uint64_t hashByElm = _hashElement(b._table[i].ptr, false); + hashIndex = hashToIndex(hashByElm); + j = hashIndex % b._nrAlloc; + if (b._table[i].hashCache != hashByElm) { + std::cout << "Alarm hashCache wrong " << i << std::endl; + } + for (k = j; k != i; ) { + if (b._table[k].ptr == nullptr || + _isEqualElementElement(b._table[i].ptr, + b._table[k].ptr)) { + ok = false; + std::cout << "Alarm unique: " << k << ", " + << i << std::endl; + } + k = incr(b, k); } - k = incr(k); } } } @@ -932,7 +998,8 @@ namespace triagens { /// @brief find an element or its place using the element hash function //////////////////////////////////////////////////////////////////////////////// - inline IndexType findElementPlace (Element const* element, + inline IndexType findElementPlace (Bucket const& b, + Element const* element, bool checkEquality, uint64_t& hashByElm) const { @@ -946,13 +1013,13 @@ namespace triagens { hashByElm = _hashElement(element, false); IndexType hashindex = hashToIndex(hashByElm); - IndexType i = hashindex % _nrAlloc; + IndexType i = hashindex % b._nrAlloc; - while (_table[i].ptr != nullptr && + while (b._table[i].ptr != nullptr && (! checkEquality || - _table[i].hashCache != hashByElm || - ! _isEqualElementElement(element, _table[i].ptr))) { - i = incr(i); + b._table[i].hashCache != hashByElm || + ! _isEqualElementElement(element, b._table[i].ptr))) { + i = incr(b, i); #ifdef TRI_INTERNAL_STATS _nrProbes++; #endif @@ -964,35 +1031,38 @@ namespace triagens { /// @brief find an element or its place by key or element identity //////////////////////////////////////////////////////////////////////////////// - IndexType lookupByElement (Element const* element) const { + IndexType lookupByElement (Element const* element, + Bucket*& buck) const { // This performs a complete lookup for an element. It returns a slot // number. This slot is either empty or contains an element that // compares equal to element. uint64_t hashByKey = _hashElement(element, true); + Bucket const& b = _buckets[hashByKey & _bucketsMask]; + buck = const_cast(&b); IndexType hashIndex = hashToIndex(hashByKey); - IndexType i = hashIndex % _nrAlloc; + IndexType i = hashIndex % b._nrAlloc; // Now find the first slot with an entry with the same key // that is the start of a linked list, or a free slot: - while (_table[i].ptr != nullptr && - (_table[i].prev != INVALID_INDEX || - _table[i].hashCache != hashByKey || - ! _isEqualElementElementByKey(element, _table[i].ptr))) { - i = incr(i); + while (b._table[i].ptr != nullptr && + (b._table[i].prev != INVALID_INDEX || + b._table[i].hashCache != hashByKey || + ! _isEqualElementElementByKey(element, b._table[i].ptr))) { + i = incr(b, i); #ifdef TRI_INTERNAL_STATS _nrProbes++; #endif } - if (_table[i].ptr != nullptr) { + if (b._table[i].ptr != nullptr) { // It might be right here! - if (_isEqualElementElement(element, _table[i].ptr)) { + if (_isEqualElementElement(element, b._table[i].ptr)) { return i; } // Now we have to look for it in its hash position: uint64_t hashByElm; - IndexType j = findElementPlace(element, true, hashByElm); + IndexType j = findElementPlace(b, element, true, hashByElm); // We have either found an equal element or nothing: return j; @@ -1022,50 +1092,50 @@ namespace triagens { /// @brief helper to invalidate a slot //////////////////////////////////////////////////////////////////////////////// - inline void invalidateEntry (IndexType i) { - _table[i] = { 0, nullptr, INVALID_INDEX, INVALID_INDEX }; + inline void invalidateEntry (Bucket& b, IndexType i) { + b._table[i] = { 0, nullptr, INVALID_INDEX, INVALID_INDEX }; } //////////////////////////////////////////////////////////////////////////////// /// @brief helper to move an entry from one slot to another //////////////////////////////////////////////////////////////////////////////// - inline void moveEntry (IndexType from, IndexType to) { + inline void moveEntry (Bucket& b, IndexType from, IndexType to) { // Moves an entry, adjusts the linked lists, but does not take care // for the hole. to must be unused. from can be any element in a // linked list. - _table[to] = _table[from]; - if (_table[to].prev != INVALID_INDEX) { - _table[_table[to].prev].next = to; + b._table[to] = b._table[from]; + if (b._table[to].prev != INVALID_INDEX) { + b._table[b._table[to].prev].next = to; } - if (_table[to].next != INVALID_INDEX) { - _table[_table[to].next].prev = to; + if (b._table[to].next != INVALID_INDEX) { + b._table[b._table[to].next].prev = to; } - invalidateEntry(from); + invalidateEntry(b, from); } //////////////////////////////////////////////////////////////////////////////// /// @brief helper to heal a hole where we deleted something //////////////////////////////////////////////////////////////////////////////// - void healHole (IndexType i) { - IndexType j = incr(i); + void healHole (Bucket& b, IndexType i) { + IndexType j = incr(b, i); - while (_table[j].ptr != nullptr) { + while (b._table[j].ptr != nullptr) { // Find out where this element ought to be: // If it is the start of one of the linked lists, we need to hash // by key, otherwise, we hash by the full identity of the element: - uint64_t hash = _hashElement(_table[j].ptr, - _table[j].prev == INVALID_INDEX); + uint64_t hash = _hashElement(b._table[j].ptr, + b._table[j].prev == INVALID_INDEX); IndexType hashIndex = hashToIndex(hash); - IndexType k = hashIndex % _nrAlloc; + IndexType k = hashIndex % b._nrAlloc; if (! isBetween(i, k, j)) { // we have to move j to i: - moveEntry(j, i); + moveEntry(b, j, i); i = j; // Now heal this hole at j, // j will be incremented right away } - j = incr(j); + j = incr(b, j); #ifdef TRI_INTERNAL_STATS _nrProbesD++; #endif