diff --git a/cpp/deglib/include/intrusive_list.h b/cpp/deglib/include/intrusive_list.h new file mode 100644 index 0000000..68f37dc --- /dev/null +++ b/cpp/deglib/include/intrusive_list.h @@ -0,0 +1,73 @@ +#pragma once + +#include + +namespace deglib::graph { + +/* +Usage: + +``` +struct Entry { + Entry* next_; // list hooks + Entry* prev_; // list hooks + // ... other members +}; + +using List = IntrusiveList; +``` + +Make sure entries added to the list have a stable address. + + +Implementation adapted from +https://github.com/facebookexperimental/libunifex/blob/main/include/unifex/detail/intrusive_list.hpp + */ +template +class IntrusiveList +{ + private: + T* head_{}; + T* tail_{}; + + public: + IntrusiveList() = default; + + IntrusiveList(const IntrusiveList&) = delete; + + IntrusiveList(IntrusiveList&& other) noexcept + : head_(std::exchange(other.head_, nullptr)), tail_(std::exchange(other.tail_, nullptr)) + { + } + + ~IntrusiveList() = default; + + IntrusiveList& operator=(const IntrusiveList&) = delete; + IntrusiveList& operator=(IntrusiveList&&) = delete; + + [[nodiscard]] bool empty() const noexcept { return head_ == nullptr; } + + void push_back(T* item) noexcept + { + item->*Prev = tail_; + item->*Next = nullptr; + if (tail_ == nullptr) + head_ = item; + else + tail_->*Next = item; + tail_ = item; + } + + [[nodiscard]] T* pop_front() noexcept + { + T* item = head_; + head_ = item->*Next; + if (head_ != nullptr) + head_->*Prev = nullptr; + else + tail_ = nullptr; + return item; + } +}; + +} // namespace deglib::graph \ No newline at end of file diff --git a/cpp/deglib/include/visited_list_pool.h b/cpp/deglib/include/visited_list_pool.h index 471d89b..472c062 100644 --- a/cpp/deglib/include/visited_list_pool.h +++ b/cpp/deglib/include/visited_list_pool.h @@ -1,9 +1,10 @@ #pragma once +#include "intrusive_list.h" + #include #include -#include -#include +#include #include /** @@ -11,14 +12,20 @@ */ namespace deglib::graph { +class VisitedListPool; + class VisitedList { private: - uint16_t current_tag_{1}; + friend VisitedListPool; + + VisitedList* next_; + VisitedList* prev_; std::unique_ptr slots_; - unsigned int num_elements_; + uint32_t num_elements_; + uint16_t current_tag_{1}; public: - explicit VisitedList(int numelements1) : slots_(std::make_unique(numelements1)), num_elements_(numelements1) {} + explicit VisitedList(uint32_t numelements1) : slots_(std::make_unique(numelements1)), num_elements_(numelements1) {} [[nodiscard]] auto* get_visited() const { return slots_.get(); @@ -31,7 +38,7 @@ class VisitedList { void reset() { ++current_tag_; if (current_tag_ == 0) { - std::fill_n(slots_.get(), num_elements_, 0); + std::fill_n(slots_.get(), num_elements_, uint16_t{}); ++current_tag_; } } @@ -39,16 +46,20 @@ class VisitedList { class VisitedListPool { private: - using ListPtr = std::unique_ptr; - - std::deque pool_; + IntrusiveList pool_; std::mutex pool_guard_; - int num_elements_; + uint32_t num_elements_; public: - VisitedListPool(int initmaxpools, int numelements) : num_elements_(numelements) { - for (int i = 0; i < initmaxpools; i++) - pool_.push_front(std::make_unique(numelements)); + VisitedListPool(uint32_t initmaxpools, uint32_t numelements) : num_elements_(numelements) { + for (uint32_t i = 0; i < initmaxpools; i++) + pool_.push_back(new VisitedList(numelements)); + } + + ~VisitedListPool() noexcept { + while (!pool_.empty()) { + delete pool_.pop_front(); + } } class FreeVisitedList { @@ -56,9 +67,9 @@ class VisitedListPool { friend VisitedListPool; VisitedListPool& pool_; - ListPtr list_; + VisitedList& list_; - FreeVisitedList(VisitedListPool& pool, ListPtr list) : pool_(pool), list_(std::move(list)) {} + FreeVisitedList(VisitedListPool& pool, VisitedList& list) : pool_(pool), list_(list) {} public: FreeVisitedList(const FreeVisitedList& other) = delete; @@ -67,36 +78,35 @@ class VisitedListPool { FreeVisitedList& operator=(FreeVisitedList&& other) = delete; ~FreeVisitedList() noexcept { - pool_.releaseVisitedList(std::move(list_)); + pool_.releaseVisitedList(list_); } auto operator->() const { - return list_.get(); + return &list_; } }; - FreeVisitedList getFreeVisitedList() { - ListPtr rez = popVisitedList(); + [[nodiscard]] FreeVisitedList getFreeVisitedList() { + auto rez = popVisitedList(); if (rez) { rez->reset(); } else { - rez = std::make_unique(num_elements_); + rez = new VisitedList(num_elements_); } - return {*this, std::move(rez)}; + return {*this, *rez}; } private: - void releaseVisitedList(ListPtr vl) { + void releaseVisitedList(VisitedList& vl) { std::unique_lock lock(pool_guard_); - pool_.push_back(std::move(vl)); + pool_.push_back(&vl); } - ListPtr popVisitedList() { - ListPtr rez; + VisitedList* popVisitedList() { + VisitedList* rez{}; std::unique_lock lock(pool_guard_); if (!pool_.empty()) { - rez = std::move(pool_.front()); - pool_.pop_front(); + rez = pool_.pop_front(); } return rez; }