efficient / libcuckoo

A high-performance, concurrent hash table
Other
1.62k stars 275 forks source link

add cursor without table lock? #142

Open Jianghi opened 2 years ago

Jianghi commented 2 years ago

will you add this feature?

ifndef _CUCKOOHASH_UNSAFE_ITER_HH

define _CUCKOOHASH_UNSAFE_ITER_HH

// inner class of cuckoohash_map class const_cursor { public: constcursor() { } bool good() const { return good; } // Return true if the iterators are from the same locked table and // location, false otherwise. bool operator==(const constcursor &it) const { if (!good && !it.good) return true; return buckets == it.buckets && index == it.index && slot == it.slot_; }

bool operator!=(const const_cursor &it) const { return !(operator==(it)); }

const_pointer operator->() const { return ©edvalue; }

// Advance the iterator to the next item in the table, or to the end // of the table. Returns the iterator at its new position. constcursor &operator++() { // 只需此处加锁即可 // Move forward until we get to a slot that is occupied, or we // get to the end ++slot; for (; index < buckets->size(); ++index) { for (; slot < slot_perbucket(); ++slot) { if ((buckets)[index].occupied(slot_)) { fill_copyed_value(); return this; } } slot_ = 0; } assert(std::makepair(index, slot_) == endpos(*buckets)); return *this; }

// Advance the iterator to the next item in the table, or to the end // of the table. Returns the iterator at its old position. const_cursor operator++(int) { const_cursor old(this); ++(this); return old; }

sizetype partition() const { return index; }

private: // cuckoohash_map's value

std::reference_wrapper map_; sizetype hp; bucketst *buckets; // local value bool good_ {false}; sizetype index {0}; sizetype slot {0}; value_type copyedvalue;

// Returns the position signifying the end of the table static std::pair<size_type, size_type> end_pos(const buckets_t &buckets) { return std::make_pair(buckets.size(), 0); }

// The private constructor is used by cuckoohashmap to create // iterators from scratch. If the given index-slot_ pair is at the // end of the table, or the given spot is occupied, stay. Otherwise, // step forward to the next data item, or to the end of the table. const_cursor(cuckoohash_map &map, size_type index, sizetype slot) noexcept : map(map), index(index), slot(slot) { hp = map.get().hashpower(); buckets = std::addressof(map.get().buckets_); if (std::makepair(index, slot_) != endpos(*buckets)) { good = true; if ((*buckets)[index].occupied(slot)) { fill_copyed_value(); } else { operator++(); } } } void fill_copyedvalue() { try { auto locker = map.get().lockone(hp, index_, normalmode()); auto ref = (*buckets)[index].kvpair(slot); const_cast<key_type>(&copyedvalue.first) = ref.first; copyedvalue.second = ref.second; } catch (...) { good_ = false; } }

friend class cuckoohash_map; };

class key_cursor { public: keycursor() {} bool good() const { return good; } // Return true if the iterators are from the same locked table and // location, false otherwise. bool operator==(const keycursor &it) const { if (!good && !it.good) return true; return buckets == it.buckets && index == it.index && slot == it.slot_; }

bool operator!=(const key_cursor &it) const { return !(operator==(it)); }

const key_type& operator*() const { return copyedkey; }

// Advance the iterator to the next item in the table, or to the end // of the table. Returns the iterator at its new position. keycursor &operator++() { // 只需此处加锁即可 // Move forward until we get to a slot that is occupied, or we // get to the end ++slot; for (; index < buckets->size(); ++index) { for (; slot < slot_perbucket(); ++slot) { if ((buckets)[index].occupied(slot_)) { fill_copyed_key(); return this; } } slot_ = 0; } assert(std::makepair(index, slot_) == endpos(*buckets)); return *this; }

// Advance the iterator to the next item in the table, or to the end // of the table. Returns the iterator at its old position. key_cursor operator++(int) { key_cursor old(this); ++(this); return old; } sizetype partition() const { return index; }

private: // cuckoohash_map's value

std::reference_wrapper map_; sizetype hp; bucketst *buckets; // local value bool good_ {false}; sizetype index {0}; sizetype slot {0}; key_type copyedkey;

// Returns the position signifying the end of the table static std::pair<size_type, size_type> end_pos(const buckets_t &buckets) { return std::make_pair(buckets.size(), 0); }

// The private constructor is used by cuckoohashmap to create // iterators from scratch. If the given index-slot_ pair is at the // end of the table, or the given spot is occupied, stay. Otherwise, // step forward to the next data item, or to the end of the table. key_cursor(cuckoohash_map &map, size_type index, sizetype slot) noexcept : map(map), index(index), slot(slot) { hp = map.get().hashpower(); buckets = std::addressof(map.get().buckets_); if (std::makepair(index, slot_) != endpos(*buckets)) { good = true; if ((*buckets)[index].occupied(slot)) { fill_copyed_key(); } else { operator++(); } } } void fill_copyedkey() { try { auto locker = map.get().lockone(hp, index_, normal_mode()); copyedkey = (*buckets)[index].key(slot); } catch (...) { good = false; } }

friend class cuckoohash_map; };

// // 注意 endpos = container.end(),每次调用 end 会有额外的成本 // auto cursor = container.begin(); // auto endpos = container.end(); // for (; cursor!= endpos; ++cursor) { // // cursor->first; // // cursor->second; // } // // 注意需要检查 cursor 的状态来判断是否完整遍历 // if (!cursor.good()) { // // 遍历过程出现 rehash, 提前结束 // } const_cursor begin() { return const_cursor(this, 0, 0); } const_cursor end() { const auto end_pos = const_cursor::endpos(buckets); return const_cursor(this, static_cast(end_pos.first), static_cast(end_pos.second)); }

// // 注意 endpos = container.end(),每次调用 end 会有额外的成本 // auto key_cursor = container.key_begin(); // auto key_endpos = container.key_end(); // int key_count = 0; // for (; key_cursor != key_endpos; ++key_cursor) { // ++key_count; // LOG_EVERY_N(INFO, 1000) << "fill key: " << key_cursor; // } // // 注意需要检查 key_cursor 的状态来判断是否完整遍历 // if (!key_cursor.good()) { // // 遍历过程出现 rehash, 提前结束 // } key_cursor key_begin() { return key_cursor(this, 0, 0); } key_cursor key_end() { const auto end_pos = key_cursor::endpos(buckets); return key_cursor(*this, static_cast(end_pos.first), static_cast(end_pos.second)); }

endif // _CUCKOOHASH_UNSAFE_ITER_HH