| File: | home/bhubbard/working/src/ceph/src/rocksdb/memtable/skiplistrep.cc |
| Warning: | line 826, column 13 The left operand of '!=' is a garbage value |
[?] Use j/k keys for keyboard navigation
| 1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. | |||
| 2 | // This source code is licensed under both the GPLv2 (found in the | |||
| 3 | // COPYING file in the root directory) and Apache 2.0 License | |||
| 4 | // (found in the LICENSE.Apache file in the root directory). | |||
| 5 | // | |||
| 6 | #include "memtable/inlineskiplist.h" | |||
| 7 | #include "db/memtable.h" | |||
| 8 | #include "rocksdb/memtablerep.h" | |||
| 9 | #include "util/arena.h" | |||
| 10 | ||||
| 11 | namespace rocksdb { | |||
| 12 | namespace { | |||
| 13 | class SkipListRep : public MemTableRep { | |||
| 14 | InlineSkipList<const MemTableRep::KeyComparator&> skip_list_; | |||
| 15 | const MemTableRep::KeyComparator& cmp_; | |||
| 16 | const SliceTransform* transform_; | |||
| 17 | const size_t lookahead_; | |||
| 18 | ||||
| 19 | friend class LookaheadIterator; | |||
| 20 | public: | |||
| 21 | explicit SkipListRep(const MemTableRep::KeyComparator& compare, | |||
| 22 | Allocator* allocator, const SliceTransform* transform, | |||
| 23 | const size_t lookahead) | |||
| 24 | : MemTableRep(allocator), | |||
| 25 | skip_list_(compare, allocator), | |||
| 26 | cmp_(compare), | |||
| 27 | transform_(transform), | |||
| 28 | lookahead_(lookahead) {} | |||
| 29 | ||||
| 30 | KeyHandle Allocate(const size_t len, char** buf) override { | |||
| 31 | *buf = skip_list_.AllocateKey(len); | |||
| 32 | return static_cast<KeyHandle>(*buf); | |||
| 33 | } | |||
| 34 | ||||
| 35 | // Insert key into the list. | |||
| 36 | // REQUIRES: nothing that compares equal to key is currently in the list. | |||
| 37 | void Insert(KeyHandle handle) override { | |||
| 38 | skip_list_.Insert(static_cast<char*>(handle)); | |||
| 39 | } | |||
| 40 | ||||
| 41 | bool InsertKey(KeyHandle handle) override { | |||
| 42 | return skip_list_.Insert(static_cast<char*>(handle)); | |||
| 43 | } | |||
| 44 | ||||
| 45 | void InsertWithHint(KeyHandle handle, void** hint) override { | |||
| 46 | skip_list_.InsertWithHint(static_cast<char*>(handle), hint); | |||
| 47 | } | |||
| 48 | ||||
| 49 | bool InsertKeyWithHint(KeyHandle handle, void** hint) override { | |||
| 50 | return skip_list_.InsertWithHint(static_cast<char*>(handle), hint); | |||
| 51 | } | |||
| 52 | ||||
| 53 | void InsertConcurrently(KeyHandle handle) override { | |||
| 54 | skip_list_.InsertConcurrently(static_cast<char*>(handle)); | |||
| 55 | } | |||
| 56 | ||||
| 57 | bool InsertKeyConcurrently(KeyHandle handle) override { | |||
| 58 | return skip_list_.InsertConcurrently(static_cast<char*>(handle)); | |||
| ||||
| 59 | } | |||
| 60 | ||||
| 61 | // Returns true iff an entry that compares equal to key is in the list. | |||
| 62 | bool Contains(const char* key) const override { | |||
| 63 | return skip_list_.Contains(key); | |||
| 64 | } | |||
| 65 | ||||
| 66 | size_t ApproximateMemoryUsage() override { | |||
| 67 | // All memory is allocated through allocator; nothing to report here | |||
| 68 | return 0; | |||
| 69 | } | |||
| 70 | ||||
| 71 | void Get(const LookupKey& k, void* callback_args, | |||
| 72 | bool (*callback_func)(void* arg, const char* entry)) override { | |||
| 73 | SkipListRep::Iterator iter(&skip_list_); | |||
| 74 | Slice dummy_slice; | |||
| 75 | for (iter.Seek(dummy_slice, k.memtable_key().data()); | |||
| 76 | iter.Valid() && callback_func(callback_args, iter.key()); iter.Next()) { | |||
| 77 | } | |||
| 78 | } | |||
| 79 | ||||
| 80 | uint64_t ApproximateNumEntries(const Slice& start_ikey, | |||
| 81 | const Slice& end_ikey) override { | |||
| 82 | std::string tmp; | |||
| 83 | uint64_t start_count = | |||
| 84 | skip_list_.EstimateCount(EncodeKey(&tmp, start_ikey)); | |||
| 85 | uint64_t end_count = skip_list_.EstimateCount(EncodeKey(&tmp, end_ikey)); | |||
| 86 | return (end_count >= start_count) ? (end_count - start_count) : 0; | |||
| 87 | } | |||
| 88 | ||||
| 89 | ~SkipListRep() override {} | |||
| 90 | ||||
| 91 | // Iteration over the contents of a skip list | |||
| 92 | class Iterator : public MemTableRep::Iterator { | |||
| 93 | InlineSkipList<const MemTableRep::KeyComparator&>::Iterator iter_; | |||
| 94 | ||||
| 95 | public: | |||
| 96 | // Initialize an iterator over the specified list. | |||
| 97 | // The returned iterator is not valid. | |||
| 98 | explicit Iterator( | |||
| 99 | const InlineSkipList<const MemTableRep::KeyComparator&>* list) | |||
| 100 | : iter_(list) {} | |||
| 101 | ||||
| 102 | ~Iterator() override {} | |||
| 103 | ||||
| 104 | // Returns true iff the iterator is positioned at a valid node. | |||
| 105 | bool Valid() const override { return iter_.Valid(); } | |||
| 106 | ||||
| 107 | // Returns the key at the current position. | |||
| 108 | // REQUIRES: Valid() | |||
| 109 | const char* key() const override { return iter_.key(); } | |||
| 110 | ||||
| 111 | // Advances to the next position. | |||
| 112 | // REQUIRES: Valid() | |||
| 113 | void Next() override { iter_.Next(); } | |||
| 114 | ||||
| 115 | // Advances to the previous position. | |||
| 116 | // REQUIRES: Valid() | |||
| 117 | void Prev() override { iter_.Prev(); } | |||
| 118 | ||||
| 119 | // Advance to the first entry with a key >= target | |||
| 120 | void Seek(const Slice& user_key, const char* memtable_key) override { | |||
| 121 | if (memtable_key != nullptr) { | |||
| 122 | iter_.Seek(memtable_key); | |||
| 123 | } else { | |||
| 124 | iter_.Seek(EncodeKey(&tmp_, user_key)); | |||
| 125 | } | |||
| 126 | } | |||
| 127 | ||||
| 128 | // Retreat to the last entry with a key <= target | |||
| 129 | void SeekForPrev(const Slice& user_key, const char* memtable_key) override { | |||
| 130 | if (memtable_key != nullptr) { | |||
| 131 | iter_.SeekForPrev(memtable_key); | |||
| 132 | } else { | |||
| 133 | iter_.SeekForPrev(EncodeKey(&tmp_, user_key)); | |||
| 134 | } | |||
| 135 | } | |||
| 136 | ||||
| 137 | // Position at the first entry in list. | |||
| 138 | // Final state of iterator is Valid() iff list is not empty. | |||
| 139 | void SeekToFirst() override { iter_.SeekToFirst(); } | |||
| 140 | ||||
| 141 | // Position at the last entry in list. | |||
| 142 | // Final state of iterator is Valid() iff list is not empty. | |||
| 143 | void SeekToLast() override { iter_.SeekToLast(); } | |||
| 144 | ||||
| 145 | protected: | |||
| 146 | std::string tmp_; // For passing to EncodeKey | |||
| 147 | }; | |||
| 148 | ||||
| 149 | // Iterator over the contents of a skip list which also keeps track of the | |||
| 150 | // previously visited node. In Seek(), it examines a few nodes after it | |||
| 151 | // first, falling back to O(log n) search from the head of the list only if | |||
| 152 | // the target key hasn't been found. | |||
| 153 | class LookaheadIterator : public MemTableRep::Iterator { | |||
| 154 | public: | |||
| 155 | explicit LookaheadIterator(const SkipListRep& rep) : | |||
| 156 | rep_(rep), iter_(&rep_.skip_list_), prev_(iter_) {} | |||
| 157 | ||||
| 158 | ~LookaheadIterator() override {} | |||
| 159 | ||||
| 160 | bool Valid() const override { return iter_.Valid(); } | |||
| 161 | ||||
| 162 | const char* key() const override { | |||
| 163 | assert(Valid())(static_cast<void> (0)); | |||
| 164 | return iter_.key(); | |||
| 165 | } | |||
| 166 | ||||
| 167 | void Next() override { | |||
| 168 | assert(Valid())(static_cast<void> (0)); | |||
| 169 | ||||
| 170 | bool advance_prev = true; | |||
| 171 | if (prev_.Valid()) { | |||
| 172 | auto k1 = rep_.UserKey(prev_.key()); | |||
| 173 | auto k2 = rep_.UserKey(iter_.key()); | |||
| 174 | ||||
| 175 | if (k1.compare(k2) == 0) { | |||
| 176 | // same user key, don't move prev_ | |||
| 177 | advance_prev = false; | |||
| 178 | } else if (rep_.transform_) { | |||
| 179 | // only advance prev_ if it has the same prefix as iter_ | |||
| 180 | auto t1 = rep_.transform_->Transform(k1); | |||
| 181 | auto t2 = rep_.transform_->Transform(k2); | |||
| 182 | advance_prev = t1.compare(t2) == 0; | |||
| 183 | } | |||
| 184 | } | |||
| 185 | ||||
| 186 | if (advance_prev) { | |||
| 187 | prev_ = iter_; | |||
| 188 | } | |||
| 189 | iter_.Next(); | |||
| 190 | } | |||
| 191 | ||||
| 192 | void Prev() override { | |||
| 193 | assert(Valid())(static_cast<void> (0)); | |||
| 194 | iter_.Prev(); | |||
| 195 | prev_ = iter_; | |||
| 196 | } | |||
| 197 | ||||
| 198 | void Seek(const Slice& internal_key, const char* memtable_key) override { | |||
| 199 | const char *encoded_key = | |||
| 200 | (memtable_key != nullptr) ? | |||
| 201 | memtable_key : EncodeKey(&tmp_, internal_key); | |||
| 202 | ||||
| 203 | if (prev_.Valid() && rep_.cmp_(encoded_key, prev_.key()) >= 0) { | |||
| 204 | // prev_.key() is smaller or equal to our target key; do a quick | |||
| 205 | // linear search (at most lookahead_ steps) starting from prev_ | |||
| 206 | iter_ = prev_; | |||
| 207 | ||||
| 208 | size_t cur = 0; | |||
| 209 | while (cur++ <= rep_.lookahead_ && iter_.Valid()) { | |||
| 210 | if (rep_.cmp_(encoded_key, iter_.key()) <= 0) { | |||
| 211 | return; | |||
| 212 | } | |||
| 213 | Next(); | |||
| 214 | } | |||
| 215 | } | |||
| 216 | ||||
| 217 | iter_.Seek(encoded_key); | |||
| 218 | prev_ = iter_; | |||
| 219 | } | |||
| 220 | ||||
| 221 | void SeekForPrev(const Slice& internal_key, | |||
| 222 | const char* memtable_key) override { | |||
| 223 | const char* encoded_key = (memtable_key != nullptr) | |||
| 224 | ? memtable_key | |||
| 225 | : EncodeKey(&tmp_, internal_key); | |||
| 226 | iter_.SeekForPrev(encoded_key); | |||
| 227 | prev_ = iter_; | |||
| 228 | } | |||
| 229 | ||||
| 230 | void SeekToFirst() override { | |||
| 231 | iter_.SeekToFirst(); | |||
| 232 | prev_ = iter_; | |||
| 233 | } | |||
| 234 | ||||
| 235 | void SeekToLast() override { | |||
| 236 | iter_.SeekToLast(); | |||
| 237 | prev_ = iter_; | |||
| 238 | } | |||
| 239 | ||||
| 240 | protected: | |||
| 241 | std::string tmp_; // For passing to EncodeKey | |||
| 242 | ||||
| 243 | private: | |||
| 244 | const SkipListRep& rep_; | |||
| 245 | InlineSkipList<const MemTableRep::KeyComparator&>::Iterator iter_; | |||
| 246 | InlineSkipList<const MemTableRep::KeyComparator&>::Iterator prev_; | |||
| 247 | }; | |||
| 248 | ||||
| 249 | MemTableRep::Iterator* GetIterator(Arena* arena = nullptr) override { | |||
| 250 | if (lookahead_ > 0) { | |||
| 251 | void *mem = | |||
| 252 | arena ? arena->AllocateAligned(sizeof(SkipListRep::LookaheadIterator)) | |||
| 253 | : operator new(sizeof(SkipListRep::LookaheadIterator)); | |||
| 254 | return new (mem) SkipListRep::LookaheadIterator(*this); | |||
| 255 | } else { | |||
| 256 | void *mem = | |||
| 257 | arena ? arena->AllocateAligned(sizeof(SkipListRep::Iterator)) | |||
| 258 | : operator new(sizeof(SkipListRep::Iterator)); | |||
| 259 | return new (mem) SkipListRep::Iterator(&skip_list_); | |||
| 260 | } | |||
| 261 | } | |||
| 262 | }; | |||
| 263 | } | |||
| 264 | ||||
| 265 | MemTableRep* SkipListFactory::CreateMemTableRep( | |||
| 266 | const MemTableRep::KeyComparator& compare, Allocator* allocator, | |||
| 267 | const SliceTransform* transform, Logger* /*logger*/) { | |||
| 268 | return new SkipListRep(compare, allocator, transform, lookahead_); | |||
| 269 | } | |||
| 270 | ||||
| 271 | } // namespace rocksdb |
| 1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. | |||||
| 2 | // This source code is licensed under both the GPLv2 (found in the | |||||
| 3 | // COPYING file in the root directory) and Apache 2.0 License | |||||
| 4 | // (found in the LICENSE.Apache file in the root directory). | |||||
| 5 | // | |||||
| 6 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. Use of | |||||
| 7 | // this source code is governed by a BSD-style license that can be found | |||||
| 8 | // in the LICENSE file. See the AUTHORS file for names of contributors. | |||||
| 9 | // | |||||
| 10 | // InlineSkipList is derived from SkipList (skiplist.h), but it optimizes | |||||
| 11 | // the memory layout by requiring that the key storage be allocated through | |||||
| 12 | // the skip list instance. For the common case of SkipList<const char*, | |||||
| 13 | // Cmp> this saves 1 pointer per skip list node and gives better cache | |||||
| 14 | // locality, at the expense of wasted padding from using AllocateAligned | |||||
| 15 | // instead of Allocate for the keys. The unused padding will be from | |||||
| 16 | // 0 to sizeof(void*)-1 bytes, and the space savings are sizeof(void*) | |||||
| 17 | // bytes, so despite the padding the space used is always less than | |||||
| 18 | // SkipList<const char*, ..>. | |||||
| 19 | // | |||||
| 20 | // Thread safety ------------- | |||||
| 21 | // | |||||
| 22 | // Writes via Insert require external synchronization, most likely a mutex. | |||||
| 23 | // InsertConcurrently can be safely called concurrently with reads and | |||||
| 24 | // with other concurrent inserts. Reads require a guarantee that the | |||||
| 25 | // InlineSkipList will not be destroyed while the read is in progress. | |||||
| 26 | // Apart from that, reads progress without any internal locking or | |||||
| 27 | // synchronization. | |||||
| 28 | // | |||||
| 29 | // Invariants: | |||||
| 30 | // | |||||
| 31 | // (1) Allocated nodes are never deleted until the InlineSkipList is | |||||
| 32 | // destroyed. This is trivially guaranteed by the code since we never | |||||
| 33 | // delete any skip list nodes. | |||||
| 34 | // | |||||
| 35 | // (2) The contents of a Node except for the next/prev pointers are | |||||
| 36 | // immutable after the Node has been linked into the InlineSkipList. | |||||
| 37 | // Only Insert() modifies the list, and it is careful to initialize a | |||||
| 38 | // node and use release-stores to publish the nodes in one or more lists. | |||||
| 39 | // | |||||
| 40 | // ... prev vs. next pointer ordering ... | |||||
| 41 | // | |||||
| 42 | ||||||
| 43 | #pragma once | |||||
| 44 | #include <assert.h> | |||||
| 45 | #include <stdlib.h> | |||||
| 46 | #include <algorithm> | |||||
| 47 | #include <atomic> | |||||
| 48 | #include <type_traits> | |||||
| 49 | #include "port/likely.h" | |||||
| 50 | #include "port/port.h" | |||||
| 51 | #include "rocksdb/slice.h" | |||||
| 52 | #include "util/allocator.h" | |||||
| 53 | #include "util/coding.h" | |||||
| 54 | #include "util/random.h" | |||||
| 55 | ||||||
| 56 | namespace rocksdb { | |||||
| 57 | ||||||
| 58 | template <class Comparator> | |||||
| 59 | class InlineSkipList { | |||||
| 60 | private: | |||||
| 61 | struct Node; | |||||
| 62 | struct Splice; | |||||
| 63 | ||||||
| 64 | public: | |||||
| 65 | using DecodedKey = \ | |||||
| 66 | typename std::remove_reference<Comparator>::type::DecodedType; | |||||
| 67 | ||||||
| 68 | static const uint16_t kMaxPossibleHeight = 32; | |||||
| 69 | ||||||
| 70 | // Create a new InlineSkipList object that will use "cmp" for comparing | |||||
| 71 | // keys, and will allocate memory using "*allocator". Objects allocated | |||||
| 72 | // in the allocator must remain allocated for the lifetime of the | |||||
| 73 | // skiplist object. | |||||
| 74 | explicit InlineSkipList(Comparator cmp, Allocator* allocator, | |||||
| 75 | int32_t max_height = 12, | |||||
| 76 | int32_t branching_factor = 4); | |||||
| 77 | ||||||
| 78 | // Allocates a key and a skip-list node, returning a pointer to the key | |||||
| 79 | // portion of the node. This method is thread-safe if the allocator | |||||
| 80 | // is thread-safe. | |||||
| 81 | char* AllocateKey(size_t key_size); | |||||
| 82 | ||||||
| 83 | // Allocate a splice using allocator. | |||||
| 84 | Splice* AllocateSplice(); | |||||
| 85 | ||||||
| 86 | // Inserts a key allocated by AllocateKey, after the actual key value | |||||
| 87 | // has been filled in. | |||||
| 88 | // | |||||
| 89 | // REQUIRES: nothing that compares equal to key is currently in the list. | |||||
| 90 | // REQUIRES: no concurrent calls to any of inserts. | |||||
| 91 | bool Insert(const char* key); | |||||
| 92 | ||||||
| 93 | // Inserts a key allocated by AllocateKey with a hint of last insert | |||||
| 94 | // position in the skip-list. If hint points to nullptr, a new hint will be | |||||
| 95 | // populated, which can be used in subsequent calls. | |||||
| 96 | // | |||||
| 97 | // It can be used to optimize the workload where there are multiple groups | |||||
| 98 | // of keys, and each key is likely to insert to a location close to the last | |||||
| 99 | // inserted key in the same group. One example is sequential inserts. | |||||
| 100 | // | |||||
| 101 | // REQUIRES: nothing that compares equal to key is currently in the list. | |||||
| 102 | // REQUIRES: no concurrent calls to any of inserts. | |||||
| 103 | bool InsertWithHint(const char* key, void** hint); | |||||
| 104 | ||||||
| 105 | // Like Insert, but external synchronization is not required. | |||||
| 106 | bool InsertConcurrently(const char* key); | |||||
| 107 | ||||||
| 108 | // Inserts a node into the skip list. key must have been allocated by | |||||
| 109 | // AllocateKey and then filled in by the caller. If UseCAS is true, | |||||
| 110 | // then external synchronization is not required, otherwise this method | |||||
| 111 | // may not be called concurrently with any other insertions. | |||||
| 112 | // | |||||
| 113 | // Regardless of whether UseCAS is true, the splice must be owned | |||||
| 114 | // exclusively by the current thread. If allow_partial_splice_fix is | |||||
| 115 | // true, then the cost of insertion is amortized O(log D), where D is | |||||
| 116 | // the distance from the splice to the inserted key (measured as the | |||||
| 117 | // number of intervening nodes). Note that this bound is very good for | |||||
| 118 | // sequential insertions! If allow_partial_splice_fix is false then | |||||
| 119 | // the existing splice will be ignored unless the current key is being | |||||
| 120 | // inserted immediately after the splice. allow_partial_splice_fix == | |||||
| 121 | // false has worse running time for the non-sequential case O(log N), | |||||
| 122 | // but a better constant factor. | |||||
| 123 | template <bool UseCAS> | |||||
| 124 | bool Insert(const char* key, Splice* splice, bool allow_partial_splice_fix); | |||||
| 125 | ||||||
| 126 | // Returns true iff an entry that compares equal to key is in the list. | |||||
| 127 | bool Contains(const char* key) const; | |||||
| 128 | ||||||
| 129 | // Return estimated number of entries smaller than `key`. | |||||
| 130 | uint64_t EstimateCount(const char* key) const; | |||||
| 131 | ||||||
| 132 | // Validate correctness of the skip-list. | |||||
| 133 | void TEST_Validate() const; | |||||
| 134 | ||||||
| 135 | // Iteration over the contents of a skip list | |||||
| 136 | class Iterator { | |||||
| 137 | public: | |||||
| 138 | // Initialize an iterator over the specified list. | |||||
| 139 | // The returned iterator is not valid. | |||||
| 140 | explicit Iterator(const InlineSkipList* list); | |||||
| 141 | ||||||
| 142 | // Change the underlying skiplist used for this iterator | |||||
| 143 | // This enables us not changing the iterator without deallocating | |||||
| 144 | // an old one and then allocating a new one | |||||
| 145 | void SetList(const InlineSkipList* list); | |||||
| 146 | ||||||
| 147 | // Returns true iff the iterator is positioned at a valid node. | |||||
| 148 | bool Valid() const; | |||||
| 149 | ||||||
| 150 | // Returns the key at the current position. | |||||
| 151 | // REQUIRES: Valid() | |||||
| 152 | const char* key() const; | |||||
| 153 | ||||||
| 154 | // Advances to the next position. | |||||
| 155 | // REQUIRES: Valid() | |||||
| 156 | void Next(); | |||||
| 157 | ||||||
| 158 | // Advances to the previous position. | |||||
| 159 | // REQUIRES: Valid() | |||||
| 160 | void Prev(); | |||||
| 161 | ||||||
| 162 | // Advance to the first entry with a key >= target | |||||
| 163 | void Seek(const char* target); | |||||
| 164 | ||||||
| 165 | // Retreat to the last entry with a key <= target | |||||
| 166 | void SeekForPrev(const char* target); | |||||
| 167 | ||||||
| 168 | // Position at the first entry in list. | |||||
| 169 | // Final state of iterator is Valid() iff list is not empty. | |||||
| 170 | void SeekToFirst(); | |||||
| 171 | ||||||
| 172 | // Position at the last entry in list. | |||||
| 173 | // Final state of iterator is Valid() iff list is not empty. | |||||
| 174 | void SeekToLast(); | |||||
| 175 | ||||||
| 176 | private: | |||||
| 177 | const InlineSkipList* list_; | |||||
| 178 | Node* node_; | |||||
| 179 | // Intentionally copyable | |||||
| 180 | }; | |||||
| 181 | ||||||
| 182 | private: | |||||
| 183 | const uint16_t kMaxHeight_; | |||||
| 184 | const uint16_t kBranching_; | |||||
| 185 | const uint32_t kScaledInverseBranching_; | |||||
| 186 | ||||||
| 187 | Allocator* const allocator_; // Allocator used for allocations of nodes | |||||
| 188 | // Immutable after construction | |||||
| 189 | Comparator const compare_; | |||||
| 190 | Node* const head_; | |||||
| 191 | ||||||
| 192 | // Modified only by Insert(). Read racily by readers, but stale | |||||
| 193 | // values are ok. | |||||
| 194 | std::atomic<int> max_height_; // Height of the entire list | |||||
| 195 | ||||||
| 196 | // seq_splice_ is a Splice used for insertions in the non-concurrent | |||||
| 197 | // case. It caches the prev and next found during the most recent | |||||
| 198 | // non-concurrent insertion. | |||||
| 199 | Splice* seq_splice_; | |||||
| 200 | ||||||
| 201 | inline int GetMaxHeight() const { | |||||
| 202 | return max_height_.load(std::memory_order_relaxed); | |||||
| 203 | } | |||||
| 204 | ||||||
| 205 | int RandomHeight(); | |||||
| 206 | ||||||
| 207 | Node* AllocateNode(size_t key_size, int height); | |||||
| 208 | ||||||
| 209 | bool Equal(const char* a, const char* b) const { | |||||
| 210 | return (compare_(a, b) == 0); | |||||
| 211 | } | |||||
| 212 | ||||||
| 213 | bool LessThan(const char* a, const char* b) const { | |||||
| 214 | return (compare_(a, b) < 0); | |||||
| 215 | } | |||||
| 216 | ||||||
| 217 | // Return true if key is greater than the data stored in "n". Null n | |||||
| 218 | // is considered infinite. n should not be head_. | |||||
| 219 | bool KeyIsAfterNode(const char* key, Node* n) const; | |||||
| 220 | bool KeyIsAfterNode(const DecodedKey& key, Node* n) const; | |||||
| 221 | ||||||
| 222 | // Returns the earliest node with a key >= key. | |||||
| 223 | // Return nullptr if there is no such node. | |||||
| 224 | Node* FindGreaterOrEqual(const char* key) const; | |||||
| 225 | ||||||
| 226 | // Return the latest node with a key < key. | |||||
| 227 | // Return head_ if there is no such node. | |||||
| 228 | // Fills prev[level] with pointer to previous node at "level" for every | |||||
| 229 | // level in [0..max_height_-1], if prev is non-null. | |||||
| 230 | Node* FindLessThan(const char* key, Node** prev = nullptr) const; | |||||
| 231 | ||||||
| 232 | // Return the latest node with a key < key on bottom_level. Start searching | |||||
| 233 | // from root node on the level below top_level. | |||||
| 234 | // Fills prev[level] with pointer to previous node at "level" for every | |||||
| 235 | // level in [bottom_level..top_level-1], if prev is non-null. | |||||
| 236 | Node* FindLessThan(const char* key, Node** prev, Node* root, int top_level, | |||||
| 237 | int bottom_level) const; | |||||
| 238 | ||||||
| 239 | // Return the last node in the list. | |||||
| 240 | // Return head_ if list is empty. | |||||
| 241 | Node* FindLast() const; | |||||
| 242 | ||||||
| 243 | // Traverses a single level of the list, setting *out_prev to the last | |||||
| 244 | // node before the key and *out_next to the first node after. Assumes | |||||
| 245 | // that the key is not present in the skip list. On entry, before should | |||||
| 246 | // point to a node that is before the key, and after should point to | |||||
| 247 | // a node that is after the key. after should be nullptr if a good after | |||||
| 248 | // node isn't conveniently available. | |||||
| 249 | template<bool prefetch_before> | |||||
| 250 | void FindSpliceForLevel(const DecodedKey& key, Node* before, Node* after, int level, | |||||
| 251 | Node** out_prev, Node** out_next); | |||||
| 252 | ||||||
| 253 | // Recomputes Splice levels from highest_level (inclusive) down to | |||||
| 254 | // lowest_level (inclusive). | |||||
| 255 | void RecomputeSpliceLevels(const DecodedKey& key, Splice* splice, | |||||
| 256 | int recompute_level); | |||||
| 257 | ||||||
| 258 | // No copying allowed | |||||
| 259 | InlineSkipList(const InlineSkipList&); | |||||
| 260 | InlineSkipList& operator=(const InlineSkipList&); | |||||
| 261 | }; | |||||
| 262 | ||||||
| 263 | // Implementation details follow | |||||
| 264 | ||||||
| 265 | template <class Comparator> | |||||
| 266 | struct InlineSkipList<Comparator>::Splice { | |||||
| 267 | // The invariant of a Splice is that prev_[i+1].key <= prev_[i].key < | |||||
| 268 | // next_[i].key <= next_[i+1].key for all i. That means that if a | |||||
| 269 | // key is bracketed by prev_[i] and next_[i] then it is bracketed by | |||||
| 270 | // all higher levels. It is _not_ required that prev_[i]->Next(i) == | |||||
| 271 | // next_[i] (it probably did at some point in the past, but intervening | |||||
| 272 | // or concurrent operations might have inserted nodes in between). | |||||
| 273 | int height_ = 0; | |||||
| 274 | Node** prev_; | |||||
| 275 | Node** next_; | |||||
| 276 | }; | |||||
| 277 | ||||||
| 278 | // The Node data type is more of a pointer into custom-managed memory than | |||||
| 279 | // a traditional C++ struct. The key is stored in the bytes immediately | |||||
| 280 | // after the struct, and the next_ pointers for nodes with height > 1 are | |||||
| 281 | // stored immediately _before_ the struct. This avoids the need to include | |||||
| 282 | // any pointer or sizing data, which reduces per-node memory overheads. | |||||
| 283 | template <class Comparator> | |||||
| 284 | struct InlineSkipList<Comparator>::Node { | |||||
| 285 | // Stores the height of the node in the memory location normally used for | |||||
| 286 | // next_[0]. This is used for passing data from AllocateKey to Insert. | |||||
| 287 | void StashHeight(const int height) { | |||||
| 288 | assert(sizeof(int) <= sizeof(next_[0]))(static_cast<void> (0)); | |||||
| 289 | memcpy(static_cast<void*>(&next_[0]), &height, sizeof(int)); | |||||
| 290 | } | |||||
| 291 | ||||||
| 292 | // Retrieves the value passed to StashHeight. Undefined after a call | |||||
| 293 | // to SetNext or NoBarrier_SetNext. | |||||
| 294 | int UnstashHeight() const { | |||||
| 295 | int rv; | |||||
| 296 | memcpy(&rv, &next_[0], sizeof(int)); | |||||
| 297 | return rv; | |||||
| 298 | } | |||||
| 299 | ||||||
| 300 | const char* Key() const { return reinterpret_cast<const char*>(&next_[1]); } | |||||
| 301 | ||||||
| 302 | // Accessors/mutators for links. Wrapped in methods so we can add | |||||
| 303 | // the appropriate barriers as necessary, and perform the necessary | |||||
| 304 | // addressing trickery for storing links below the Node in memory. | |||||
| 305 | Node* Next(int n) { | |||||
| 306 | assert(n >= 0)(static_cast<void> (0)); | |||||
| 307 | // Use an 'acquire load' so that we observe a fully initialized | |||||
| 308 | // version of the returned Node. | |||||
| 309 | return ((&next_[0] - n)->load(std::memory_order_acquire)); | |||||
| 310 | } | |||||
| 311 | ||||||
| 312 | void SetNext(int n, Node* x) { | |||||
| 313 | assert(n >= 0)(static_cast<void> (0)); | |||||
| 314 | // Use a 'release store' so that anybody who reads through this | |||||
| 315 | // pointer observes a fully initialized version of the inserted node. | |||||
| 316 | (&next_[0] - n)->store(x, std::memory_order_release); | |||||
| 317 | } | |||||
| 318 | ||||||
| 319 | bool CASNext(int n, Node* expected, Node* x) { | |||||
| 320 | assert(n >= 0)(static_cast<void> (0)); | |||||
| 321 | return (&next_[0] - n)->compare_exchange_strong(expected, x); | |||||
| 322 | } | |||||
| 323 | ||||||
| 324 | // No-barrier variants that can be safely used in a few locations. | |||||
| 325 | Node* NoBarrier_Next(int n) { | |||||
| 326 | assert(n >= 0)(static_cast<void> (0)); | |||||
| 327 | return (&next_[0] - n)->load(std::memory_order_relaxed); | |||||
| 328 | } | |||||
| 329 | ||||||
| 330 | void NoBarrier_SetNext(int n, Node* x) { | |||||
| 331 | assert(n >= 0)(static_cast<void> (0)); | |||||
| 332 | (&next_[0] - n)->store(x, std::memory_order_relaxed); | |||||
| 333 | } | |||||
| 334 | ||||||
| 335 | // Insert node after prev on specific level. | |||||
| 336 | void InsertAfter(Node* prev, int level) { | |||||
| 337 | // NoBarrier_SetNext() suffices since we will add a barrier when | |||||
| 338 | // we publish a pointer to "this" in prev. | |||||
| 339 | NoBarrier_SetNext(level, prev->NoBarrier_Next(level)); | |||||
| 340 | prev->SetNext(level, this); | |||||
| 341 | } | |||||
| 342 | ||||||
| 343 | private: | |||||
| 344 | // next_[0] is the lowest level link (level 0). Higher levels are | |||||
| 345 | // stored _earlier_, so level 1 is at next_[-1]. | |||||
| 346 | std::atomic<Node*> next_[1]; | |||||
| 347 | }; | |||||
| 348 | ||||||
| 349 | template <class Comparator> | |||||
| 350 | inline InlineSkipList<Comparator>::Iterator::Iterator( | |||||
| 351 | const InlineSkipList* list) { | |||||
| 352 | SetList(list); | |||||
| 353 | } | |||||
| 354 | ||||||
| 355 | template <class Comparator> | |||||
| 356 | inline void InlineSkipList<Comparator>::Iterator::SetList( | |||||
| 357 | const InlineSkipList* list) { | |||||
| 358 | list_ = list; | |||||
| 359 | node_ = nullptr; | |||||
| 360 | } | |||||
| 361 | ||||||
| 362 | template <class Comparator> | |||||
| 363 | inline bool InlineSkipList<Comparator>::Iterator::Valid() const { | |||||
| 364 | return node_ != nullptr; | |||||
| 365 | } | |||||
| 366 | ||||||
| 367 | template <class Comparator> | |||||
| 368 | inline const char* InlineSkipList<Comparator>::Iterator::key() const { | |||||
| 369 | assert(Valid())(static_cast<void> (0)); | |||||
| 370 | return node_->Key(); | |||||
| 371 | } | |||||
| 372 | ||||||
| 373 | template <class Comparator> | |||||
| 374 | inline void InlineSkipList<Comparator>::Iterator::Next() { | |||||
| 375 | assert(Valid())(static_cast<void> (0)); | |||||
| 376 | node_ = node_->Next(0); | |||||
| 377 | } | |||||
| 378 | ||||||
| 379 | template <class Comparator> | |||||
| 380 | inline void InlineSkipList<Comparator>::Iterator::Prev() { | |||||
| 381 | // Instead of using explicit "prev" links, we just search for the | |||||
| 382 | // last node that falls before key. | |||||
| 383 | assert(Valid())(static_cast<void> (0)); | |||||
| 384 | node_ = list_->FindLessThan(node_->Key()); | |||||
| 385 | if (node_ == list_->head_) { | |||||
| 386 | node_ = nullptr; | |||||
| 387 | } | |||||
| 388 | } | |||||
| 389 | ||||||
| 390 | template <class Comparator> | |||||
| 391 | inline void InlineSkipList<Comparator>::Iterator::Seek(const char* target) { | |||||
| 392 | node_ = list_->FindGreaterOrEqual(target); | |||||
| 393 | } | |||||
| 394 | ||||||
| 395 | template <class Comparator> | |||||
| 396 | inline void InlineSkipList<Comparator>::Iterator::SeekForPrev( | |||||
| 397 | const char* target) { | |||||
| 398 | Seek(target); | |||||
| 399 | if (!Valid()) { | |||||
| 400 | SeekToLast(); | |||||
| 401 | } | |||||
| 402 | while (Valid() && list_->LessThan(target, key())) { | |||||
| 403 | Prev(); | |||||
| 404 | } | |||||
| 405 | } | |||||
| 406 | ||||||
| 407 | template <class Comparator> | |||||
| 408 | inline void InlineSkipList<Comparator>::Iterator::SeekToFirst() { | |||||
| 409 | node_ = list_->head_->Next(0); | |||||
| 410 | } | |||||
| 411 | ||||||
| 412 | template <class Comparator> | |||||
| 413 | inline void InlineSkipList<Comparator>::Iterator::SeekToLast() { | |||||
| 414 | node_ = list_->FindLast(); | |||||
| 415 | if (node_ == list_->head_) { | |||||
| 416 | node_ = nullptr; | |||||
| 417 | } | |||||
| 418 | } | |||||
| 419 | ||||||
| 420 | template <class Comparator> | |||||
| 421 | int InlineSkipList<Comparator>::RandomHeight() { | |||||
| 422 | auto rnd = Random::GetTLSInstance(); | |||||
| 423 | ||||||
| 424 | // Increase height with probability 1 in kBranching | |||||
| 425 | int height = 1; | |||||
| 426 | while (height < kMaxHeight_ && height < kMaxPossibleHeight && | |||||
| 427 | rnd->Next() < kScaledInverseBranching_) { | |||||
| 428 | height++; | |||||
| 429 | } | |||||
| 430 | assert(height > 0)(static_cast<void> (0)); | |||||
| 431 | assert(height <= kMaxHeight_)(static_cast<void> (0)); | |||||
| 432 | assert(height <= kMaxPossibleHeight)(static_cast<void> (0)); | |||||
| 433 | return height; | |||||
| 434 | } | |||||
| 435 | ||||||
| 436 | template <class Comparator> | |||||
| 437 | bool InlineSkipList<Comparator>::KeyIsAfterNode(const char* key, | |||||
| 438 | Node* n) const { | |||||
| 439 | // nullptr n is considered infinite | |||||
| 440 | assert(n != head_)(static_cast<void> (0)); | |||||
| 441 | return (n != nullptr) && (compare_(n->Key(), key) < 0); | |||||
| 442 | } | |||||
| 443 | ||||||
| 444 | template <class Comparator> | |||||
| 445 | bool InlineSkipList<Comparator>::KeyIsAfterNode(const DecodedKey& key, | |||||
| 446 | Node* n) const { | |||||
| 447 | // nullptr n is considered infinite | |||||
| 448 | assert(n != head_)(static_cast<void> (0)); | |||||
| 449 | return (n != nullptr) && (compare_(n->Key(), key) < 0); | |||||
| 450 | } | |||||
| 451 | ||||||
| 452 | template <class Comparator> | |||||
| 453 | typename InlineSkipList<Comparator>::Node* | |||||
| 454 | InlineSkipList<Comparator>::FindGreaterOrEqual(const char* key) const { | |||||
| 455 | // Note: It looks like we could reduce duplication by implementing | |||||
| 456 | // this function as FindLessThan(key)->Next(0), but we wouldn't be able | |||||
| 457 | // to exit early on equality and the result wouldn't even be correct. | |||||
| 458 | // A concurrent insert might occur after FindLessThan(key) but before | |||||
| 459 | // we get a chance to call Next(0). | |||||
| 460 | Node* x = head_; | |||||
| 461 | int level = GetMaxHeight() - 1; | |||||
| 462 | Node* last_bigger = nullptr; | |||||
| 463 | const DecodedKey key_decoded = compare_.decode_key(key); | |||||
| 464 | while (true) { | |||||
| 465 | Node* next = x->Next(level); | |||||
| 466 | if (next != nullptr) { | |||||
| 467 | PREFETCH(next->Next(level), 0, 1)__builtin_prefetch(next->Next(level), 0, 1); | |||||
| 468 | } | |||||
| 469 | // Make sure the lists are sorted | |||||
| 470 | assert(x == head_ || next == nullptr || KeyIsAfterNode(next->Key(), x))(static_cast<void> (0)); | |||||
| 471 | // Make sure we haven't overshot during our search | |||||
| 472 | assert(x == head_ || KeyIsAfterNode(key_decoded, x))(static_cast<void> (0)); | |||||
| 473 | int cmp = (next == nullptr || next == last_bigger) | |||||
| 474 | ? 1 | |||||
| 475 | : compare_(next->Key(), key_decoded); | |||||
| 476 | if (cmp == 0 || (cmp > 0 && level == 0)) { | |||||
| 477 | return next; | |||||
| 478 | } else if (cmp < 0) { | |||||
| 479 | // Keep searching in this list | |||||
| 480 | x = next; | |||||
| 481 | } else { | |||||
| 482 | // Switch to next list, reuse compare_() result | |||||
| 483 | last_bigger = next; | |||||
| 484 | level--; | |||||
| 485 | } | |||||
| 486 | } | |||||
| 487 | } | |||||
| 488 | ||||||
| 489 | template <class Comparator> | |||||
| 490 | typename InlineSkipList<Comparator>::Node* | |||||
| 491 | InlineSkipList<Comparator>::FindLessThan(const char* key, Node** prev) const { | |||||
| 492 | return FindLessThan(key, prev, head_, GetMaxHeight(), 0); | |||||
| 493 | } | |||||
| 494 | ||||||
| 495 | template <class Comparator> | |||||
| 496 | typename InlineSkipList<Comparator>::Node* | |||||
| 497 | InlineSkipList<Comparator>::FindLessThan(const char* key, Node** prev, | |||||
| 498 | Node* root, int top_level, | |||||
| 499 | int bottom_level) const { | |||||
| 500 | assert(top_level > bottom_level)(static_cast<void> (0)); | |||||
| 501 | int level = top_level - 1; | |||||
| 502 | Node* x = root; | |||||
| 503 | // KeyIsAfter(key, last_not_after) is definitely false | |||||
| 504 | Node* last_not_after = nullptr; | |||||
| 505 | const DecodedKey key_decoded = compare_.decode_key(key); | |||||
| 506 | while (true) { | |||||
| 507 | assert(x != nullptr)(static_cast<void> (0)); | |||||
| 508 | Node* next = x->Next(level); | |||||
| 509 | if (next != nullptr) { | |||||
| 510 | PREFETCH(next->Next(level), 0, 1)__builtin_prefetch(next->Next(level), 0, 1); | |||||
| 511 | } | |||||
| 512 | assert(x == head_ || next == nullptr || KeyIsAfterNode(next->Key(), x))(static_cast<void> (0)); | |||||
| 513 | assert(x == head_ || KeyIsAfterNode(key_decoded, x))(static_cast<void> (0)); | |||||
| 514 | if (next != last_not_after && KeyIsAfterNode(key_decoded, next)) { | |||||
| 515 | // Keep searching in this list | |||||
| 516 | assert(next != nullptr)(static_cast<void> (0)); | |||||
| 517 | x = next; | |||||
| 518 | } else { | |||||
| 519 | if (prev != nullptr) { | |||||
| 520 | prev[level] = x; | |||||
| 521 | } | |||||
| 522 | if (level == bottom_level) { | |||||
| 523 | return x; | |||||
| 524 | } else { | |||||
| 525 | // Switch to next list, reuse KeyIsAfterNode() result | |||||
| 526 | last_not_after = next; | |||||
| 527 | level--; | |||||
| 528 | } | |||||
| 529 | } | |||||
| 530 | } | |||||
| 531 | } | |||||
| 532 | ||||||
| 533 | template <class Comparator> | |||||
| 534 | typename InlineSkipList<Comparator>::Node* | |||||
| 535 | InlineSkipList<Comparator>::FindLast() const { | |||||
| 536 | Node* x = head_; | |||||
| 537 | int level = GetMaxHeight() - 1; | |||||
| 538 | while (true) { | |||||
| 539 | Node* next = x->Next(level); | |||||
| 540 | if (next == nullptr) { | |||||
| 541 | if (level == 0) { | |||||
| 542 | return x; | |||||
| 543 | } else { | |||||
| 544 | // Switch to next list | |||||
| 545 | level--; | |||||
| 546 | } | |||||
| 547 | } else { | |||||
| 548 | x = next; | |||||
| 549 | } | |||||
| 550 | } | |||||
| 551 | } | |||||
| 552 | ||||||
| 553 | template <class Comparator> | |||||
| 554 | uint64_t InlineSkipList<Comparator>::EstimateCount(const char* key) const { | |||||
| 555 | uint64_t count = 0; | |||||
| 556 | ||||||
| 557 | Node* x = head_; | |||||
| 558 | int level = GetMaxHeight() - 1; | |||||
| 559 | const DecodedKey key_decoded = compare_.decode_key(key); | |||||
| 560 | while (true) { | |||||
| 561 | assert(x == head_ || compare_(x->Key(), key_decoded) < 0)(static_cast<void> (0)); | |||||
| 562 | Node* next = x->Next(level); | |||||
| 563 | if (next != nullptr) { | |||||
| 564 | PREFETCH(next->Next(level), 0, 1)__builtin_prefetch(next->Next(level), 0, 1); | |||||
| 565 | } | |||||
| 566 | if (next == nullptr || compare_(next->Key(), key_decoded) >= 0) { | |||||
| 567 | if (level == 0) { | |||||
| 568 | return count; | |||||
| 569 | } else { | |||||
| 570 | // Switch to next list | |||||
| 571 | count *= kBranching_; | |||||
| 572 | level--; | |||||
| 573 | } | |||||
| 574 | } else { | |||||
| 575 | x = next; | |||||
| 576 | count++; | |||||
| 577 | } | |||||
| 578 | } | |||||
| 579 | } | |||||
| 580 | ||||||
| 581 | template <class Comparator> | |||||
| 582 | InlineSkipList<Comparator>::InlineSkipList(const Comparator cmp, | |||||
| 583 | Allocator* allocator, | |||||
| 584 | int32_t max_height, | |||||
| 585 | int32_t branching_factor) | |||||
| 586 | : kMaxHeight_(static_cast<uint16_t>(max_height)), | |||||
| 587 | kBranching_(static_cast<uint16_t>(branching_factor)), | |||||
| 588 | kScaledInverseBranching_((Random::kMaxNext + 1) / kBranching_), | |||||
| 589 | allocator_(allocator), | |||||
| 590 | compare_(cmp), | |||||
| 591 | head_(AllocateNode(0, max_height)), | |||||
| 592 | max_height_(1), | |||||
| 593 | seq_splice_(AllocateSplice()) { | |||||
| 594 | assert(max_height > 0 && kMaxHeight_ == static_cast<uint32_t>(max_height))(static_cast<void> (0)); | |||||
| 595 | assert(branching_factor > 1 &&(static_cast<void> (0)) | |||||
| 596 | kBranching_ == static_cast<uint32_t>(branching_factor))(static_cast<void> (0)); | |||||
| 597 | assert(kScaledInverseBranching_ > 0)(static_cast<void> (0)); | |||||
| 598 | ||||||
| 599 | for (int i = 0; i < kMaxHeight_; ++i) { | |||||
| 600 | head_->SetNext(i, nullptr); | |||||
| 601 | } | |||||
| 602 | } | |||||
| 603 | ||||||
| 604 | template <class Comparator> | |||||
| 605 | char* InlineSkipList<Comparator>::AllocateKey(size_t key_size) { | |||||
| 606 | return const_cast<char*>(AllocateNode(key_size, RandomHeight())->Key()); | |||||
| 607 | } | |||||
| 608 | ||||||
| 609 | template <class Comparator> | |||||
| 610 | typename InlineSkipList<Comparator>::Node* | |||||
| 611 | InlineSkipList<Comparator>::AllocateNode(size_t key_size, int height) { | |||||
| 612 | auto prefix = sizeof(std::atomic<Node*>) * (height - 1); | |||||
| 613 | ||||||
| 614 | // prefix is space for the height - 1 pointers that we store before | |||||
| 615 | // the Node instance (next_[-(height - 1) .. -1]). Node starts at | |||||
| 616 | // raw + prefix, and holds the bottom-mode (level 0) skip list pointer | |||||
| 617 | // next_[0]. key_size is the bytes for the key, which comes just after | |||||
| 618 | // the Node. | |||||
| 619 | char* raw = allocator_->AllocateAligned(prefix + sizeof(Node) + key_size); | |||||
| 620 | Node* x = reinterpret_cast<Node*>(raw + prefix); | |||||
| 621 | ||||||
| 622 | // Once we've linked the node into the skip list we don't actually need | |||||
| 623 | // to know its height, because we can implicitly use the fact that we | |||||
| 624 | // traversed into a node at level h to known that h is a valid level | |||||
| 625 | // for that node. We need to convey the height to the Insert step, | |||||
| 626 | // however, so that it can perform the proper links. Since we're not | |||||
| 627 | // using the pointers at the moment, StashHeight temporarily borrow | |||||
| 628 | // storage from next_[0] for that purpose. | |||||
| 629 | x->StashHeight(height); | |||||
| 630 | return x; | |||||
| 631 | } | |||||
| 632 | ||||||
| 633 | template <class Comparator> | |||||
| 634 | typename InlineSkipList<Comparator>::Splice* | |||||
| 635 | InlineSkipList<Comparator>::AllocateSplice() { | |||||
| 636 | // size of prev_ and next_ | |||||
| 637 | size_t array_size = sizeof(Node*) * (kMaxHeight_ + 1); | |||||
| 638 | char* raw = allocator_->AllocateAligned(sizeof(Splice) + array_size * 2); | |||||
| 639 | Splice* splice = reinterpret_cast<Splice*>(raw); | |||||
| 640 | splice->height_ = 0; | |||||
| 641 | splice->prev_ = reinterpret_cast<Node**>(raw + sizeof(Splice)); | |||||
| 642 | splice->next_ = reinterpret_cast<Node**>(raw + sizeof(Splice) + array_size); | |||||
| 643 | return splice; | |||||
| 644 | } | |||||
| 645 | ||||||
| 646 | template <class Comparator> | |||||
| 647 | bool InlineSkipList<Comparator>::Insert(const char* key) { | |||||
| 648 | return Insert<false>(key, seq_splice_, false); | |||||
| 649 | } | |||||
| 650 | ||||||
| 651 | template <class Comparator> | |||||
| 652 | bool InlineSkipList<Comparator>::InsertConcurrently(const char* key) { | |||||
| 653 | Node* prev[kMaxPossibleHeight]; | |||||
| 654 | Node* next[kMaxPossibleHeight]; | |||||
| 655 | Splice splice; | |||||
| 656 | splice.prev_ = prev; | |||||
| 657 | splice.next_ = next; | |||||
| 658 | return Insert<true>(key, &splice, false); | |||||
| 659 | } | |||||
| 660 | ||||||
| 661 | template <class Comparator> | |||||
| 662 | bool InlineSkipList<Comparator>::InsertWithHint(const char* key, void** hint) { | |||||
| 663 | assert(hint != nullptr)(static_cast<void> (0)); | |||||
| 664 | Splice* splice = reinterpret_cast<Splice*>(*hint); | |||||
| 665 | if (splice == nullptr) { | |||||
| 666 | splice = AllocateSplice(); | |||||
| 667 | *hint = reinterpret_cast<void*>(splice); | |||||
| 668 | } | |||||
| 669 | return Insert<false>(key, splice, true); | |||||
| 670 | } | |||||
| 671 | ||||||
| 672 | template <class Comparator> | |||||
| 673 | template <bool prefetch_before> | |||||
| 674 | void InlineSkipList<Comparator>::FindSpliceForLevel(const DecodedKey& key, | |||||
| 675 | Node* before, Node* after, | |||||
| 676 | int level, Node** out_prev, | |||||
| 677 | Node** out_next) { | |||||
| 678 | while (true) { | |||||
| 679 | Node* next = before->Next(level); | |||||
| 680 | if (next != nullptr) { | |||||
| 681 | PREFETCH(next->Next(level), 0, 1)__builtin_prefetch(next->Next(level), 0, 1); | |||||
| 682 | } | |||||
| 683 | if (prefetch_before == true) { | |||||
| 684 | if (next != nullptr && level>0) { | |||||
| 685 | PREFETCH(next->Next(level-1), 0, 1)__builtin_prefetch(next->Next(level-1), 0, 1); | |||||
| 686 | } | |||||
| 687 | } | |||||
| 688 | assert(before == head_ || next == nullptr ||(static_cast<void> (0)) | |||||
| 689 | KeyIsAfterNode(next->Key(), before))(static_cast<void> (0)); | |||||
| 690 | assert(before == head_ || KeyIsAfterNode(key, before))(static_cast<void> (0)); | |||||
| 691 | if (next == after || !KeyIsAfterNode(key, next)) { | |||||
| 692 | // found it | |||||
| 693 | *out_prev = before; | |||||
| 694 | *out_next = next; | |||||
| 695 | return; | |||||
| 696 | } | |||||
| 697 | before = next; | |||||
| 698 | } | |||||
| 699 | } | |||||
| 700 | ||||||
| 701 | template <class Comparator> | |||||
| 702 | void InlineSkipList<Comparator>::RecomputeSpliceLevels(const DecodedKey& key, | |||||
| 703 | Splice* splice, | |||||
| 704 | int recompute_level) { | |||||
| 705 | assert(recompute_level > 0)(static_cast<void> (0)); | |||||
| 706 | assert(recompute_level <= splice->height_)(static_cast<void> (0)); | |||||
| 707 | for (int i = recompute_level - 1; i >= 0; --i) { | |||||
| 708 | FindSpliceForLevel<true>(key, splice->prev_[i + 1], splice->next_[i + 1], i, | |||||
| 709 | &splice->prev_[i], &splice->next_[i]); | |||||
| 710 | } | |||||
| 711 | } | |||||
| 712 | ||||||
| 713 | template <class Comparator> | |||||
| 714 | template <bool UseCAS> | |||||
| 715 | bool InlineSkipList<Comparator>::Insert(const char* key, Splice* splice, | |||||
| 716 | bool allow_partial_splice_fix) { | |||||
| 717 | Node* x = reinterpret_cast<Node*>(const_cast<char*>(key)) - 1; | |||||
| 718 | const DecodedKey key_decoded = compare_.decode_key(key); | |||||
| 719 | int height = x->UnstashHeight(); | |||||
| 720 | assert(height >= 1 && height <= kMaxHeight_)(static_cast<void> (0)); | |||||
| 721 | ||||||
| 722 | int max_height = max_height_.load(std::memory_order_relaxed); | |||||
| 723 | while (height > max_height) { | |||||
| 724 | if (max_height_.compare_exchange_weak(max_height, height)) { | |||||
| 725 | // successfully updated it | |||||
| 726 | max_height = height; | |||||
| 727 | break; | |||||
| 728 | } | |||||
| 729 | // else retry, possibly exiting the loop because somebody else | |||||
| 730 | // increased it | |||||
| 731 | } | |||||
| 732 | assert(max_height <= kMaxPossibleHeight)(static_cast<void> (0)); | |||||
| 733 | ||||||
| 734 | int recompute_height = 0; | |||||
| 735 | if (splice->height_ < max_height) { | |||||
| 736 | // Either splice has never been used or max_height has grown since | |||||
| 737 | // last use. We could potentially fix it in the latter case, but | |||||
| 738 | // that is tricky. | |||||
| 739 | splice->prev_[max_height] = head_; | |||||
| 740 | splice->next_[max_height] = nullptr; | |||||
| 741 | splice->height_ = max_height; | |||||
| 742 | recompute_height = max_height; | |||||
| 743 | } else { | |||||
| 744 | // Splice is a valid proper-height splice that brackets some | |||||
| 745 | // key, but does it bracket this one? We need to validate it and | |||||
| 746 | // recompute a portion of the splice (levels 0..recompute_height-1) | |||||
| 747 | // that is a superset of all levels that don't bracket the new key. | |||||
| 748 | // Several choices are reasonable, because we have to balance the work | |||||
| 749 | // saved against the extra comparisons required to validate the Splice. | |||||
| 750 | // | |||||
| 751 | // One strategy is just to recompute all of orig_splice_height if the | |||||
| 752 | // bottom level isn't bracketing. This pessimistically assumes that | |||||
| 753 | // we will either get a perfect Splice hit (increasing sequential | |||||
| 754 | // inserts) or have no locality. | |||||
| 755 | // | |||||
| 756 | // Another strategy is to walk up the Splice's levels until we find | |||||
| 757 | // a level that brackets the key. This strategy lets the Splice | |||||
| 758 | // hint help for other cases: it turns insertion from O(log N) into | |||||
| 759 | // O(log D), where D is the number of nodes in between the key that | |||||
| 760 | // produced the Splice and the current insert (insertion is aided | |||||
| 761 | // whether the new key is before or after the splice). If you have | |||||
| 762 | // a way of using a prefix of the key to map directly to the closest | |||||
| 763 | // Splice out of O(sqrt(N)) Splices and we make it so that splices | |||||
| 764 | // can also be used as hints during read, then we end up with Oshman's | |||||
| 765 | // and Shavit's SkipTrie, which has O(log log N) lookup and insertion | |||||
| 766 | // (compare to O(log N) for skip list). | |||||
| 767 | // | |||||
| 768 | // We control the pessimistic strategy with allow_partial_splice_fix. | |||||
| 769 | // A good strategy is probably to be pessimistic for seq_splice_, | |||||
| 770 | // optimistic if the caller actually went to the work of providing | |||||
| 771 | // a Splice. | |||||
| 772 | while (recompute_height < max_height) { | |||||
| 773 | if (splice->prev_[recompute_height]->Next(recompute_height) != | |||||
| 774 | splice->next_[recompute_height]) { | |||||
| 775 | // splice isn't tight at this level, there must have been some inserts | |||||
| 776 | // to this | |||||
| 777 | // location that didn't update the splice. We might only be a little | |||||
| 778 | // stale, but if | |||||
| 779 | // the splice is very stale it would be O(N) to fix it. We haven't used | |||||
| 780 | // up any of | |||||
| 781 | // our budget of comparisons, so always move up even if we are | |||||
| 782 | // pessimistic about | |||||
| 783 | // our chances of success. | |||||
| 784 | ++recompute_height; | |||||
| 785 | } else if (splice->prev_[recompute_height] != head_ && | |||||
| 786 | !KeyIsAfterNode(key_decoded, | |||||
| 787 | splice->prev_[recompute_height])) { | |||||
| 788 | // key is from before splice | |||||
| 789 | if (allow_partial_splice_fix) { | |||||
| 790 | // skip all levels with the same node without more comparisons | |||||
| 791 | Node* bad = splice->prev_[recompute_height]; | |||||
| 792 | while (splice->prev_[recompute_height] == bad) { | |||||
| 793 | ++recompute_height; | |||||
| 794 | } | |||||
| 795 | } else { | |||||
| 796 | // we're pessimistic, recompute everything | |||||
| 797 | recompute_height = max_height; | |||||
| 798 | } | |||||
| 799 | } else if (KeyIsAfterNode(key_decoded, | |||||
| 800 | splice->next_[recompute_height])) { | |||||
| 801 | // key is from after splice | |||||
| 802 | if (allow_partial_splice_fix) { | |||||
| 803 | Node* bad = splice->next_[recompute_height]; | |||||
| 804 | while (splice->next_[recompute_height] == bad) { | |||||
| 805 | ++recompute_height; | |||||
| 806 | } | |||||
| 807 | } else { | |||||
| 808 | recompute_height = max_height; | |||||
| 809 | } | |||||
| 810 | } else { | |||||
| 811 | // this level brackets the key, we won! | |||||
| 812 | break; | |||||
| 813 | } | |||||
| 814 | } | |||||
| 815 | } | |||||
| 816 | assert(recompute_height <= max_height)(static_cast<void> (0)); | |||||
| 817 | if (recompute_height > 0) { | |||||
| 818 | RecomputeSpliceLevels(key_decoded, splice, recompute_height); | |||||
| 819 | } | |||||
| 820 | ||||||
| 821 | bool splice_is_valid = true; | |||||
| 822 | if (UseCAS) { | |||||
| 823 | for (int i = 0; i < height; ++i) { | |||||
| 824 | while (true) { | |||||
| 825 | // Checking for duplicate keys on the level 0 is sufficient | |||||
| 826 | if (UNLIKELY(i == 0 && splice->next_[i] != nullptr &&(__builtin_expect((i == 0 && splice->next_[i] != nullptr && compare_(x->Key(), splice->next_[i]->Key ()) >= 0), 0)) | |||||
| ||||||
| 827 | compare_(x->Key(), splice->next_[i]->Key()) >= 0)(__builtin_expect((i == 0 && splice->next_[i] != nullptr && compare_(x->Key(), splice->next_[i]->Key ()) >= 0), 0))) { | |||||
| 828 | // duplicate key | |||||
| 829 | return false; | |||||
| 830 | } | |||||
| 831 | if (UNLIKELY(i == 0 && splice->prev_[i] != head_ &&(__builtin_expect((i == 0 && splice->prev_[i] != head_ && compare_(splice->prev_[i]->Key(), x->Key ()) >= 0), 0)) | |||||
| 832 | compare_(splice->prev_[i]->Key(), x->Key()) >= 0)(__builtin_expect((i == 0 && splice->prev_[i] != head_ && compare_(splice->prev_[i]->Key(), x->Key ()) >= 0), 0))) { | |||||
| 833 | // duplicate key | |||||
| 834 | return false; | |||||
| 835 | } | |||||
| 836 | assert(splice->next_[i] == nullptr ||(static_cast<void> (0)) | |||||
| 837 | compare_(x->Key(), splice->next_[i]->Key()) < 0)(static_cast<void> (0)); | |||||
| 838 | assert(splice->prev_[i] == head_ ||(static_cast<void> (0)) | |||||
| 839 | compare_(splice->prev_[i]->Key(), x->Key()) < 0)(static_cast<void> (0)); | |||||
| 840 | x->NoBarrier_SetNext(i, splice->next_[i]); | |||||
| 841 | if (splice->prev_[i]->CASNext(i, splice->next_[i], x)) { | |||||
| 842 | // success | |||||
| 843 | break; | |||||
| 844 | } | |||||
| 845 | // CAS failed, we need to recompute prev and next. It is unlikely | |||||
| 846 | // to be helpful to try to use a different level as we redo the | |||||
| 847 | // search, because it should be unlikely that lots of nodes have | |||||
| 848 | // been inserted between prev[i] and next[i]. No point in using | |||||
| 849 | // next[i] as the after hint, because we know it is stale. | |||||
| 850 | FindSpliceForLevel<false>(key_decoded, splice->prev_[i], nullptr, i, | |||||
| 851 | &splice->prev_[i], &splice->next_[i]); | |||||
| 852 | ||||||
| 853 | // Since we've narrowed the bracket for level i, we might have | |||||
| 854 | // violated the Splice constraint between i and i-1. Make sure | |||||
| 855 | // we recompute the whole thing next time. | |||||
| 856 | if (i > 0) { | |||||
| 857 | splice_is_valid = false; | |||||
| 858 | } | |||||
| 859 | } | |||||
| 860 | } | |||||
| 861 | } else { | |||||
| 862 | for (int i = 0; i < height; ++i) { | |||||
| 863 | if (i >= recompute_height && | |||||
| 864 | splice->prev_[i]->Next(i) != splice->next_[i]) { | |||||
| 865 | FindSpliceForLevel<false>(key_decoded, splice->prev_[i], nullptr, i, | |||||
| 866 | &splice->prev_[i], &splice->next_[i]); | |||||
| 867 | } | |||||
| 868 | // Checking for duplicate keys on the level 0 is sufficient | |||||
| 869 | if (UNLIKELY(i == 0 && splice->next_[i] != nullptr &&(__builtin_expect((i == 0 && splice->next_[i] != nullptr && compare_(x->Key(), splice->next_[i]->Key ()) >= 0), 0)) | |||||
| 870 | compare_(x->Key(), splice->next_[i]->Key()) >= 0)(__builtin_expect((i == 0 && splice->next_[i] != nullptr && compare_(x->Key(), splice->next_[i]->Key ()) >= 0), 0))) { | |||||
| 871 | // duplicate key | |||||
| 872 | return false; | |||||
| 873 | } | |||||
| 874 | if (UNLIKELY(i == 0 && splice->prev_[i] != head_ &&(__builtin_expect((i == 0 && splice->prev_[i] != head_ && compare_(splice->prev_[i]->Key(), x->Key ()) >= 0), 0)) | |||||
| 875 | compare_(splice->prev_[i]->Key(), x->Key()) >= 0)(__builtin_expect((i == 0 && splice->prev_[i] != head_ && compare_(splice->prev_[i]->Key(), x->Key ()) >= 0), 0))) { | |||||
| 876 | // duplicate key | |||||
| 877 | return false; | |||||
| 878 | } | |||||
| 879 | assert(splice->next_[i] == nullptr ||(static_cast<void> (0)) | |||||
| 880 | compare_(x->Key(), splice->next_[i]->Key()) < 0)(static_cast<void> (0)); | |||||
| 881 | assert(splice->prev_[i] == head_ ||(static_cast<void> (0)) | |||||
| 882 | compare_(splice->prev_[i]->Key(), x->Key()) < 0)(static_cast<void> (0)); | |||||
| 883 | assert(splice->prev_[i]->Next(i) == splice->next_[i])(static_cast<void> (0)); | |||||
| 884 | x->NoBarrier_SetNext(i, splice->next_[i]); | |||||
| 885 | splice->prev_[i]->SetNext(i, x); | |||||
| 886 | } | |||||
| 887 | } | |||||
| 888 | if (splice_is_valid) { | |||||
| 889 | for (int i = 0; i < height; ++i) { | |||||
| 890 | splice->prev_[i] = x; | |||||
| 891 | } | |||||
| 892 | assert(splice->prev_[splice->height_] == head_)(static_cast<void> (0)); | |||||
| 893 | assert(splice->next_[splice->height_] == nullptr)(static_cast<void> (0)); | |||||
| 894 | for (int i = 0; i < splice->height_; ++i) { | |||||
| 895 | assert(splice->next_[i] == nullptr ||(static_cast<void> (0)) | |||||
| 896 | compare_(key, splice->next_[i]->Key()) < 0)(static_cast<void> (0)); | |||||
| 897 | assert(splice->prev_[i] == head_ ||(static_cast<void> (0)) | |||||
| 898 | compare_(splice->prev_[i]->Key(), key) <= 0)(static_cast<void> (0)); | |||||
| 899 | assert(splice->prev_[i + 1] == splice->prev_[i] ||(static_cast<void> (0)) | |||||
| 900 | splice->prev_[i + 1] == head_ ||(static_cast<void> (0)) | |||||
| 901 | compare_(splice->prev_[i + 1]->Key(), splice->prev_[i]->Key()) <(static_cast<void> (0)) | |||||
| 902 | 0)(static_cast<void> (0)); | |||||
| 903 | assert(splice->next_[i + 1] == splice->next_[i] ||(static_cast<void> (0)) | |||||
| 904 | splice->next_[i + 1] == nullptr ||(static_cast<void> (0)) | |||||
| 905 | compare_(splice->next_[i]->Key(), splice->next_[i + 1]->Key()) <(static_cast<void> (0)) | |||||
| 906 | 0)(static_cast<void> (0)); | |||||
| 907 | } | |||||
| 908 | } else { | |||||
| 909 | splice->height_ = 0; | |||||
| 910 | } | |||||
| 911 | return true; | |||||
| 912 | } | |||||
| 913 | ||||||
| 914 | template <class Comparator> | |||||
| 915 | bool InlineSkipList<Comparator>::Contains(const char* key) const { | |||||
| 916 | Node* x = FindGreaterOrEqual(key); | |||||
| 917 | if (x != nullptr && Equal(key, x->Key())) { | |||||
| 918 | return true; | |||||
| 919 | } else { | |||||
| 920 | return false; | |||||
| 921 | } | |||||
| 922 | } | |||||
| 923 | ||||||
| 924 | template <class Comparator> | |||||
| 925 | void InlineSkipList<Comparator>::TEST_Validate() const { | |||||
| 926 | // Interate over all levels at the same time, and verify nodes appear in | |||||
| 927 | // the right order, and nodes appear in upper level also appear in lower | |||||
| 928 | // levels. | |||||
| 929 | Node* nodes[kMaxPossibleHeight]; | |||||
| 930 | int max_height = GetMaxHeight(); | |||||
| 931 | assert(max_height > 0)(static_cast<void> (0)); | |||||
| 932 | for (int i = 0; i < max_height; i++) { | |||||
| 933 | nodes[i] = head_; | |||||
| 934 | } | |||||
| 935 | while (nodes[0] != nullptr) { | |||||
| 936 | Node* l0_next = nodes[0]->Next(0); | |||||
| 937 | if (l0_next == nullptr) { | |||||
| 938 | break; | |||||
| 939 | } | |||||
| 940 | assert(nodes[0] == head_ || compare_(nodes[0]->Key(), l0_next->Key()) < 0)(static_cast<void> (0)); | |||||
| 941 | nodes[0] = l0_next; | |||||
| 942 | ||||||
| 943 | int i = 1; | |||||
| 944 | while (i < max_height) { | |||||
| 945 | Node* next = nodes[i]->Next(i); | |||||
| 946 | if (next == nullptr) { | |||||
| 947 | break; | |||||
| 948 | } | |||||
| 949 | auto cmp = compare_(nodes[0]->Key(), next->Key()); | |||||
| 950 | assert(cmp <= 0)(static_cast<void> (0)); | |||||
| 951 | if (cmp == 0) { | |||||
| 952 | assert(next == nodes[0])(static_cast<void> (0)); | |||||
| 953 | nodes[i] = next; | |||||
| 954 | } else { | |||||
| 955 | break; | |||||
| 956 | } | |||||
| 957 | i++; | |||||
| 958 | } | |||||
| 959 | } | |||||
| 960 | for (int i = 1; i < max_height; i++) { | |||||
| 961 | assert(nodes[i] != nullptr && nodes[i]->Next(i) == nullptr)(static_cast<void> (0)); | |||||
| 962 | } | |||||
| 963 | } | |||||
| 964 | ||||||
| 965 | } // namespace rocksdb |