From 4438413f48d6854c951836696d456f67df7e32ec Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 5 Oct 2019 12:31:56 +0100 Subject: [PATCH] Linkgraph: Replace RB-tree with B-tree in MCF Dijkstra --- src/linkgraph/linkgraphjob.cpp | 16 ++--- src/linkgraph/linkgraphjob.h | 17 +++-- src/linkgraph/mcf.cpp | 120 ++++----------------------------- 3 files changed, 33 insertions(+), 120 deletions(-) diff --git a/src/linkgraph/linkgraphjob.cpp b/src/linkgraph/linkgraphjob.cpp index 21c9712097..588d1b5ef7 100644 --- a/src/linkgraph/linkgraphjob.cpp +++ b/src/linkgraph/linkgraphjob.cpp @@ -277,10 +277,10 @@ void Path::Fork(Path *base, uint cap, int free_cap, uint dist) this->free_capacity = min(base->free_capacity, free_cap); this->distance = base->distance + dist; assert(this->distance > 0); - if (this->parent != base) { + if (this->GetParent() != base) { this->Detach(); - this->parent = base; - this->parent->num_children++; + this->SetParent(base); + base->num_children++; } this->origin = base->origin; } @@ -295,8 +295,8 @@ void Path::Fork(Path *base, uint cap, int free_cap, uint dist) */ uint Path::AddFlow(uint new_flow, LinkGraphJob &job, uint max_saturation) { - if (this->parent != nullptr) { - LinkGraphJob::Edge edge = job[this->parent->node][this->node]; + if (this->GetParent() != nullptr) { + LinkGraphJob::Edge edge = job[this->GetParent()->node][this->node]; if (max_saturation != UINT_MAX) { uint usable_cap = edge.Capacity() * max_saturation / 100; if (usable_cap > edge.Flow()) { @@ -305,9 +305,9 @@ uint Path::AddFlow(uint new_flow, LinkGraphJob &job, uint max_saturation) return 0; } } - new_flow = this->parent->AddFlow(new_flow, job, max_saturation); + new_flow = this->GetParent()->AddFlow(new_flow, job, max_saturation); if (this->flow == 0 && new_flow > 0) { - job[this->parent->node].Paths().push_back(this); + job[this->GetParent()->node].Paths().push_back(this); } edge.AddFlow(new_flow); } @@ -325,6 +325,6 @@ Path::Path(NodeID n, bool source) : capacity(source ? UINT_MAX : 0), free_capacity(source ? INT_MAX : INT_MIN), flow(0), node(n), origin(source ? n : INVALID_NODE), - num_children(0), parent(nullptr) + num_children(0), parent_storage(0) {} diff --git a/src/linkgraph/linkgraphjob.h b/src/linkgraph/linkgraphjob.h index a33d7a19a7..220c454d68 100644 --- a/src/linkgraph/linkgraphjob.h +++ b/src/linkgraph/linkgraphjob.h @@ -393,7 +393,7 @@ public: inline NodeID GetOrigin() const { return this->origin; } /** Get the parent leg of this one. */ - inline Path *GetParent() { return this->parent; } + inline Path *GetParent() { return reinterpret_cast(this->parent_storage & ~1); } /** Get the overall capacity of the path. */ inline uint GetCapacity() const { return this->capacity; } @@ -442,15 +442,18 @@ public: */ inline void Detach() { - if (this->parent != nullptr) { - this->parent->num_children--; - this->parent = nullptr; + if (this->GetParent() != nullptr) { + this->GetParent()->num_children--; + this->SetParent(nullptr); } } uint AddFlow(uint f, LinkGraphJob &job, uint max_saturation); void Fork(Path *base, uint cap, int free_cap, uint dist); + inline bool GetAnnosSetFlag() const { return HasBit(this->parent_storage, 0); } + inline void SetAnnosSetFlag(bool flag) { SB(this->parent_storage, 0, 1, flag ? 1 : 0); } + protected: /** @@ -469,7 +472,11 @@ protected: NodeID node; ///< Link graph node this leg passes. NodeID origin; ///< Link graph node this path originates from. uint num_children; ///< Number of child legs that have been forked from this path. - Path *parent; ///< Parent leg of this one. + + uintptr_t parent_storage; ///< Parent leg of this one, flag in LSB of pointer + + /** Get the parent leg of this one. */ + inline void SetParent(Path *parent) { this->parent_storage = reinterpret_cast(parent) | (this->parent_storage & 1); } }; #endif /* LINKGRAPHJOB_H */ diff --git a/src/linkgraph/mcf.cpp b/src/linkgraph/mcf.cpp index 0c03b930f6..50d3efc751 100644 --- a/src/linkgraph/mcf.cpp +++ b/src/linkgraph/mcf.cpp @@ -22,6 +22,7 @@ public: NodeID node_id; AnnoSetItem(Tannotation *anno) : anno_ptr(anno), cached_annotation(anno->GetAnnotation()), node_id(anno->GetNode()) {} + AnnoSetItem() : anno_ptr(nullptr), cached_annotation(0), node_id(INVALID_NODE) {} }; /** @@ -263,98 +264,6 @@ bool CapacityAnnotation::IsBetter(const CapacityAnnotation *base, uint cap, } } -#ifdef CUSTOM_ALLOCATOR -/** - * Storage for AnnoSetAllocator instances - */ -struct AnnoSetAllocatorStore { - std::vector used_blocks; - void *current_block; - size_t next_position; - void *last_freed; - - AnnoSetAllocatorStore() : current_block(nullptr), next_position(0), last_freed(nullptr) {} - - ~AnnoSetAllocatorStore() - { - for (std::vector::iterator i = used_blocks.begin(); i != used_blocks.end(); ++i) { - free(*i); - } - } -}; - -/** - * Custom allocator specifically for use with MultiCommodityFlow::Dijkstra::AnnoSet - * This allocates RB-set nodes in contiguous blocks, and frees all allocated nodes when destructed - * If a node is deallocated, it is returned in the next allocation, this is so that the same node - * can be re-used across a call to Path::Fork - */ -template -struct AnnoSetAllocator { - static const size_t block_size = 1024; - - AnnoSetAllocatorStore &store; - - void NewBlock() - { - store.current_block = MallocT(block_size); - store.next_position = 0; - store.used_blocks.push_back(store.current_block); - } - - typedef Ttype value_type; - - template - struct rebind { - typedef AnnoSetAllocator other; - }; - - AnnoSetAllocator(AnnoSetAllocatorStore &store) : store(store) {} - - template - AnnoSetAllocator(const AnnoSetAllocator &other) : store(other.store) {} - - Ttype* allocate(size_t n) - { - if (store.current_block == nullptr) NewBlock(); - - assert(n == 1); - if (store.last_freed != nullptr) { - Ttype* out = static_cast(store.last_freed); - store.last_freed = nullptr; - return out; - } - - if (store.next_position == block_size) NewBlock(); - - Ttype* next = static_cast(store.current_block) + store.next_position; - store.next_position++; - return next; - } - - void deallocate(Ttype* p, size_t n) - { - store.last_freed = p; - } -}; -#endif - -/** - * Annotation wrapper class which also stores an iterator to the AnnoSet node which points to this annotation - * This is to enable erasing the AnnoSet node when calling Path::Fork without having to search the set - */ -template -struct AnnosWrapper : public Tannotation { -#ifdef CUSTOM_ALLOCATOR - typename std::set, typename Tannotation::Comparator, AnnoSetAllocator > >::iterator self_iter; -#else - typename std::set, typename Tannotation::Comparator>::iterator self_iter; -#endif - - - AnnosWrapper(NodeID n, bool source = false) : Tannotation(n, source) {} -}; - /** * A slightly modified Dijkstra algorithm. Grades the paths not necessarily by * distance, but by the value Tannotation computes. It uses the max_saturation @@ -367,31 +276,27 @@ struct AnnosWrapper : public Tannotation { template void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector &paths) { -#ifdef CUSTOM_ALLOCATOR - typedef std::set, typename Tannotation::Comparator, AnnoSetAllocator > > AnnoSet; - AnnoSetAllocatorStore annos_store; - AnnoSet annos = AnnoSet(typename Tannotation::Comparator(), AnnoSetAllocator(annos_store)); -#else - typedef std::set, typename Tannotation::Comparator> AnnoSet; + typedef btree::btree_set, typename Tannotation::Comparator> AnnoSet; AnnoSet annos = AnnoSet(typename Tannotation::Comparator()); -#endif Tedge_iterator iter(this->job); uint size = this->job.Size(); paths.resize(size, nullptr); - this->job.path_allocator.SetParameters(sizeof(AnnosWrapper), (8192 - 32) / sizeof(AnnosWrapper)); + this->job.path_allocator.SetParameters(sizeof(Tannotation), (8192 - 32) / sizeof(Tannotation)); for (NodeID node = 0; node < size; ++node) { - AnnosWrapper *anno = new (this->job.path_allocator.Allocate()) AnnosWrapper(node, node == source_node); + Tannotation *anno = new (this->job.path_allocator.Allocate()) Tannotation(node, node == source_node); anno->UpdateAnnotation(); - anno->self_iter = (node == source_node) ? annos.insert(AnnoSetItem(anno)).first : annos.end(); // only insert the source node, the other nodes will be added as reached + if (node == source_node) { + annos.insert(AnnoSetItem(anno)).first; + anno->SetAnnosSetFlag(true); + } paths[node] = anno; } while (!annos.empty()) { typename AnnoSet::iterator i = annos.begin(); - AnnosWrapper *source = static_cast *>(i->anno_ptr); + Tannotation *source = i->anno_ptr; annos.erase(i); - source->self_iter = annos.end(); NodeID from = source->GetNode(); iter.SetNode(source_node, from); for (NodeID to = iter.Next(); to != INVALID_NODE; to = iter.Next()) { @@ -405,12 +310,13 @@ void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector &paths) } /* punish in-between stops a little */ uint distance = DistanceMaxPlusManhattan(this->job[from].XY(), this->job[to].XY()) + 1; - AnnosWrapper *dest = static_cast *>(paths[to]); + Tannotation *dest = static_cast(paths[to]); if (dest->IsBetter(source, capacity, capacity - edge.Flow(), distance)) { - if (dest->self_iter != annos.end()) annos.erase(dest->self_iter); + if (dest->GetAnnosSetFlag()) annos.erase(AnnoSetItem(dest)); dest->Fork(source, capacity, capacity - edge.Flow(), distance); dest->UpdateAnnotation(); - dest->self_iter = annos.insert(AnnoSetItem(dest)).first; + annos.insert(AnnoSetItem(dest)); + dest->SetAnnosSetFlag(true); } } }