Linkgraph: Replace RB-tree with B-tree in MCF Dijkstra

This commit is contained in:
Jonathan G Rennison
2019-10-05 12:31:56 +01:00
parent 455e694e79
commit 4438413f48
3 changed files with 33 additions and 120 deletions

View File

@@ -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<void *> 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<void *>::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<class Ttype>
struct AnnoSetAllocator {
static const size_t block_size = 1024;
AnnoSetAllocatorStore &store;
void NewBlock()
{
store.current_block = MallocT<Ttype>(block_size);
store.next_position = 0;
store.used_blocks.push_back(store.current_block);
}
typedef Ttype value_type;
template<typename Tother>
struct rebind {
typedef AnnoSetAllocator<Tother> other;
};
AnnoSetAllocator(AnnoSetAllocatorStore &store) : store(store) {}
template<typename Tother>
AnnoSetAllocator(const AnnoSetAllocator<Tother> &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<Ttype*>(store.last_freed);
store.last_freed = nullptr;
return out;
}
if (store.next_position == block_size) NewBlock();
Ttype* next = static_cast<Ttype*>(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<class Tannotation>
struct AnnosWrapper : public Tannotation {
#ifdef CUSTOM_ALLOCATOR
typename std::set<AnnoSetItem<Tannotation>, typename Tannotation::Comparator, AnnoSetAllocator<AnnoSetItem<Tannotation> > >::iterator self_iter;
#else
typename std::set<AnnoSetItem<Tannotation>, 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<class Tannotation, class Tedge_iterator>
void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector &paths)
{
#ifdef CUSTOM_ALLOCATOR
typedef std::set<AnnoSetItem<Tannotation>, typename Tannotation::Comparator, AnnoSetAllocator<AnnoSetItem<Tannotation> > > AnnoSet;
AnnoSetAllocatorStore annos_store;
AnnoSet annos = AnnoSet(typename Tannotation::Comparator(), AnnoSetAllocator<Tannotation *>(annos_store));
#else
typedef std::set<AnnoSetItem<Tannotation>, typename Tannotation::Comparator> AnnoSet;
typedef btree::btree_set<AnnoSetItem<Tannotation>, 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<Tannotation>), (8192 - 32) / sizeof(AnnosWrapper<Tannotation>));
this->job.path_allocator.SetParameters(sizeof(Tannotation), (8192 - 32) / sizeof(Tannotation));
for (NodeID node = 0; node < size; ++node) {
AnnosWrapper<Tannotation> *anno = new (this->job.path_allocator.Allocate()) AnnosWrapper<Tannotation>(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<Tannotation>(anno)).first : annos.end(); // only insert the source node, the other nodes will be added as reached
if (node == source_node) {
annos.insert(AnnoSetItem<Tannotation>(anno)).first;
anno->SetAnnosSetFlag(true);
}
paths[node] = anno;
}
while (!annos.empty()) {
typename AnnoSet::iterator i = annos.begin();
AnnosWrapper<Tannotation> *source = static_cast<AnnosWrapper<Tannotation> *>(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<Tannotation> *dest = static_cast<AnnosWrapper<Tannotation> *>(paths[to]);
Tannotation *dest = static_cast<Tannotation *>(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<Tannotation>(dest));
dest->Fork(source, capacity, capacity - edge.Flow(), distance);
dest->UpdateAnnotation();
dest->self_iter = annos.insert(AnnoSetItem<Tannotation>(dest)).first;
annos.insert(AnnoSetItem<Tannotation>(dest));
dest->SetAnnosSetFlag(true);
}
}
}