diff --git a/src/linkgraph/mcf.cpp b/src/linkgraph/mcf.cpp index ecdf792afe..2d44c79ba8 100644 --- a/src/linkgraph/mcf.cpp +++ b/src/linkgraph/mcf.cpp @@ -9,6 +9,20 @@ typedef std::map PathViaMap; +/** + * This is a wrapper around Tannotation* which also stores a cache of GetAnnotation() and GetNode() + * to remove the need dereference the Tannotation* pointer when sorting/inseting/erasing in MultiCommodityFlow::Dijkstra::AnnoSet + */ +template +class AnnoSetItem { +public: + Tannotation *anno_ptr; + typename Tannotation::AnnotationValueType cached_annotation; + NodeID node_id; + + AnnoSetItem(Tannotation *anno) : anno_ptr(anno), cached_annotation(anno->GetAnnotation()), node_id(anno->GetNode()) {} +}; + /** * Distance-based annotation for use in the Dijkstra algorithm. This is close * to the original meaning of "annotation" in this context. Paths are rated @@ -16,6 +30,7 @@ typedef std::map PathViaMap; */ class DistanceAnnotation : public Path { public: + typedef uint AnnotationValueType; /** * Constructor. @@ -41,7 +56,7 @@ public: * Comparator for std containers. */ struct Comparator { - bool operator()(const DistanceAnnotation *x, const DistanceAnnotation *y) const; + bool operator()(const AnnoSetItem &x, const AnnoSetItem &y) const; }; }; @@ -53,8 +68,8 @@ public: */ class CapacityAnnotation : public Path { int cached_annotation; - public: + typedef int AnnotationValueType; /** * Constructor. @@ -83,7 +98,7 @@ public: * Comparator for std containers. */ struct Comparator { - bool operator()(const CapacityAnnotation *x, const CapacityAnnotation *y) const; + bool operator()(const AnnoSetItem &x, const AnnoSetItem &y) const; }; }; @@ -246,6 +261,91 @@ bool CapacityAnnotation::IsBetter(const CapacityAnnotation *base, uint cap, } } +/** + * Storage for AnnoSetAllocator instances + */ +struct AnnoSetAllocatorStore { + std::vector used_blocks; + void *current_block; + size_t next_position; + void *last_freed; + + AnnoSetAllocatorStore() : current_block(NULL), next_position(0), last_freed(NULL) {} + + ~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 == NULL) NewBlock(); + + assert(n == 1); + if (store.last_freed != NULL) { + Ttype* out = static_cast(store.last_freed); + store.last_freed = NULL; + 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; + } +}; + +/** + * 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 { + typename std::set, typename Tannotation::Comparator, AnnoSetAllocator > >::iterator self_iter; + + 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 @@ -258,21 +358,23 @@ bool CapacityAnnotation::IsBetter(const CapacityAnnotation *base, uint cap, template void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector &paths) { - typedef std::set AnnoSet; + typedef std::set, typename Tannotation::Comparator, AnnoSetAllocator > > AnnoSet; Tedge_iterator iter(this->job); uint size = this->job.Size(); - AnnoSet annos; + AnnoSetAllocatorStore annos_store; + AnnoSet annos = AnnoSet(typename Tannotation::Comparator(), AnnoSetAllocator(annos_store)); paths.resize(size, NULL); for (NodeID node = 0; node < size; ++node) { - Tannotation *anno = new Tannotation(node, node == source_node); + AnnosWrapper *anno = new AnnosWrapper(node, node == source_node); anno->UpdateAnnotation(); - annos.insert(anno); + anno->self_iter = annos.insert(AnnoSetItem(anno)).first; paths[node] = anno; } while (!annos.empty()) { typename AnnoSet::iterator i = annos.begin(); - Tannotation *source = *i; + AnnosWrapper *source = static_cast *>(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()) { @@ -286,12 +388,12 @@ 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; - Tannotation *dest = static_cast(paths[to]); + AnnosWrapper *dest = static_cast *>(paths[to]); if (dest->IsBetter(source, capacity, capacity - edge.Flow(), distance)) { - annos.erase(dest); + if (dest->self_iter != annos.end()) annos.erase(dest->self_iter); dest->Fork(source, capacity, capacity - edge.Flow(), distance); dest->UpdateAnnotation(); - annos.insert(dest); + dest->self_iter = annos.insert(AnnoSetItem(dest)).first; } } } @@ -579,11 +681,11 @@ bool Greater(T x_anno, T y_anno, NodeID x, NodeID y) * @param y Second capacity annotation. * @return If x is better than y. */ -bool CapacityAnnotation::Comparator::operator()(const CapacityAnnotation *x, - const CapacityAnnotation *y) const +bool CapacityAnnotation::Comparator::operator()(const AnnoSetItem &x, + const AnnoSetItem &y) const { - return x != y && Greater(x->GetAnnotation(), y->GetAnnotation(), - x->GetNode(), y->GetNode()); + return x.anno_ptr != y.anno_ptr && Greater(x.cached_annotation, y.cached_annotation, + x.node_id, y.node_id); } /** @@ -592,9 +694,9 @@ bool CapacityAnnotation::Comparator::operator()(const CapacityAnnotation *x, * @param y Second distance annotation. * @return If x is better than y. */ -bool DistanceAnnotation::Comparator::operator()(const DistanceAnnotation *x, - const DistanceAnnotation *y) const +bool DistanceAnnotation::Comparator::operator()(const AnnoSetItem &x, + const AnnoSetItem &y) const { - return x != y && !Greater(x->GetAnnotation(), y->GetAnnotation(), - x->GetNode(), y->GetNode()); + return x.anno_ptr != y.anno_ptr && !Greater(x.cached_annotation, y.cached_annotation, + x.node_id, y.node_id); }