diff --git a/arangod/V8Server/V8Traverser.cpp b/arangod/V8Server/V8Traverser.cpp index 1cd22f60d5..79044dacc4 100644 --- a/arangod/V8Server/V8Traverser.cpp +++ b/arangod/V8Server/V8Traverser.cpp @@ -45,8 +45,6 @@ using namespace std; using namespace triagens::basics; using namespace triagens::arango; -std::mutex m; - class SimpleEdgeExpander { private: @@ -100,13 +98,8 @@ class SimpleEdgeExpander { return edgeIdPrefix.append(key); }; - void operator() ( Traverser::VertexId source, - Traverser::Direction dir, - vector& result - ) { - //std::lock_guard guard(m); - - // cout << "Hallole: " << id << endl; + void operator() (Traverser::VertexId source, + vector& result) { std::vector edges; // Process Vertex Id! size_t split; @@ -133,19 +126,19 @@ class SimpleEdgeExpander { // cout << edges.size() << endl; } - std::unordered_map candidates; + std::unordered_map candidates; Traverser::VertexId from; Traverser::VertexId to; - std::unordered_map::const_iterator cand; + std::unordered_map::const_iterator cand; if (usesDist) { for (size_t j = 0; j < edges.size(); ++j) { from = extractFromId(edges[j]); - to = extractFromId(edges[j]); + to = extractToId(edges[j]); if (from != source) { candidates.find(from); if (cand == candidates.end()) { // Add weight - candidates.emplace(from, Traverser::Neighbor(from, edges[j]._id, 1)); + candidates.emplace(from, Traverser::Step(to, from, 1, extractEdgeId(edges[j]))); } else { // Compare weight } @@ -153,7 +146,7 @@ class SimpleEdgeExpander { candidates.find(to); if (cand == candidates.end()) { // Add weight - candidates.emplace(to, Traverser::Neighbor(to, edges[j]._id, 1)); + candidates.emplace(to, Traverser::Step(from, to, 1, extractEdgeId(edges[j]))); } else { // Compare weight } @@ -166,12 +159,12 @@ class SimpleEdgeExpander { if (from != source) { candidates.find(from); if (cand == candidates.end()) { - candidates.emplace(from, Traverser::Neighbor(from, edges[j]._id, 1)); + candidates.emplace(from, Traverser::Step(to, from, 1, extractEdgeId(edges[j]))); } } else if (to != source) { candidates.find(to); if (cand == candidates.end()) { - candidates.emplace(to, Traverser::Neighbor(to, edges[j]._id, 1)); + candidates.emplace(to, Traverser::Step(from, to, 1, extractEdgeId(edges[j]))); } } } @@ -329,7 +322,7 @@ void TRI_RunDijkstraSearch (const v8::FunctionCallbackInfo& args) { SimpleEdgeExpander backwardExpander(TRI_EDGE_IN, ecol, edgeCollectionName, &resolver2, "B"); Traverser traverser(forwardExpander, backwardExpander); - unique_ptr path(traverser.ShortestPath(startVertex, targetVertex)); + unique_ptr path(traverser.shortestPath(startVertex, targetVertex)); if (path.get() == nullptr) { res = trx.finish(res); v8::EscapableHandleScope scope(isolate); diff --git a/lib/Basics/Traverser.cpp b/lib/Basics/Traverser.cpp index b8402501b9..cef39ae32a 100644 --- a/lib/Basics/Traverser.cpp +++ b/lib/Basics/Traverser.cpp @@ -23,6 +23,7 @@ /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Michael Hackstein +/// @author Max Neunhoeffer /// @author Copyright 2014-2015, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// @@ -58,59 +59,73 @@ class Searcher : public Thread { private: - void insertNeighbor (Traverser::ThreadInfo& info, - Traverser::VertexId& neighbor, + void insertNeighbor (Traverser::VertexId& neighbor, Traverser::VertexId& predecessor, Traverser::EdgeId& edge, Traverser::EdgeWeight weight) { - std::lock_guard guard(info.mutex); - auto it = info.lookup.find(neighbor); + std::lock_guard guard(_myInfo._mutex); + Traverser::Step* s = _myInfo._pq.lookup(neighbor); // Not found, so insert it: - if (it == info.lookup.end()) { - info.lookup.emplace( - neighbor, - Traverser::LookupInfo(weight, edge, predecessor) - ); - info.queue.insert( - Traverser::QueueInfo(neighbor, weight) - ); + if (s == nullptr) { + _myInfo._pq.insert(neighbor, + Traverser::Step(neighbor, predecessor, + weight, edge)); return; } - if (it->second.done) { + if (s->_done) { return; } - if (it->second.weight > weight) { - Traverser::QueueInfo q(neighbor, it->second.weight); - info.queue.erase(q); - q.weight = weight; - info.queue.insert(q); - it->second.weight = weight; + if (s->_weight > weight) { + _myInfo._pq.lowerWeight(neighbor, weight); } } //////////////////////////////////////////////////////////////////////////////// -/// @brief Lookup a neighbor in the list of our peer. +/// @brief Lookup our current vertex in the data of our peer. //////////////////////////////////////////////////////////////////////////////// - void lookupPeer (Traverser::ThreadInfo& info, - Traverser::VertexId& neighbor, + void lookupPeer (Traverser::VertexId& vertex, Traverser::EdgeWeight weight) { - std::lock_guard guard(info.mutex); - auto it = info.lookup.find(neighbor); - if (it == info.lookup.end()) { + std::lock_guard guard(_peerInfo._mutex); + Traverser::Step* s = _peerInfo._pq.lookup(vertex); + if (s == nullptr) { + // Not found, nothing more to do return; } - Traverser::EdgeWeight total = it->second.weight + weight; - if (total < _traverser->highscore) { - _traverser->highscore = total; + Traverser::EdgeWeight total = s->_weight + weight; + + // Update the highscore: + std::lock_guard guard2(_traverser->_resultMutex); + if (!_traverser->_highscoreSet || total < _traverser->_highscore) { + _traverser->_highscoreSet = true; + _traverser->_highscore = total; } - if (it->second.done && total <= _traverser->highscore) { - std::lock_guard guard(_traverser->resultMutex); - _traverser->intermediate = neighbor; - _traverser->bingo = true; + + // Now the highscore is set! + + // Did we find a solution together with the other thread? + if (s->_done) { + if (total <= _traverser->_highscore) { + _traverser->_intermediate = vertex; + _traverser->_bingo = true; + } + // We found a way, but somebody else found a better way, so + // this is not the shortest path + return; + } + + // Did we find a solution on our own? This is for the single thread + // case and for the case that the other thread is too slow to even + // finish its own start vertex! + if (s->_weight == 0) { + // We have found the target, we have finished all vertices with + // a smaller weight than this one (and did not succeed), so this + // must be a best solution: + _traverser->_intermediate = vertex; + _traverser->_bingo = true; } } @@ -123,30 +138,27 @@ class Searcher : public Thread { virtual void run () { - auto nextVertexIt = _myInfo.queue.begin(); - std::vector neighbors; + Traverser::VertexId v; + Traverser::Step s; + bool b = _myInfo._pq.popMinimal(v, s, true); + + std::vector neighbors; // Iterate while no bingo found and // there still is a vertex on the stack. - while (!_traverser->bingo && nextVertexIt != _myInfo.queue.end()) { - auto nextVertex = *nextVertexIt; - _myInfo.queue.erase(nextVertexIt); + while (!_traverser->_bingo && b) { neighbors.clear(); - _expander(nextVertex.vertex, neighbors); + _expander(v, neighbors); for (auto& neighbor : neighbors) { - insertNeighbor(_myInfo, neighbor.neighbor, nextVertex.vertex, - neighbor.edge, nextVertex.weight + neighbor.weight); + insertNeighbor(neighbor._vertex, v, neighbor._edge, + s._weight + neighbor._weight); } - lookupPeer(_peerInfo, nextVertex.vertex, nextVertex.weight); - _myInfo.mutex.lock(); - // Can move nextVertexLookup up? - auto nextVertexLookup = _myInfo.lookup.find(nextVertex.vertex); + lookupPeer(v, s._weight); - TRI_ASSERT(nextVertexLookup != _myInfo.lookup.end()); - - nextVertexLookup->second.done = true; - _myInfo.mutex.unlock(); - nextVertexIt = _myInfo.queue.begin(); + std::lock_guard guard(_myInfo._mutex); + Traverser::Step* s2 = _myInfo._pq.lookup(v); + s2->_done = true; + b = _myInfo._pq.popMinimal(v, s, true); } } }; @@ -155,62 +167,60 @@ class Searcher : public Thread { /// @brief return the shortest path between the start and target vertex. //////////////////////////////////////////////////////////////////////////////// -Traverser::Path* Traverser::ShortestPath (VertexId const& start, - VertexId const& target) { +Traverser::Path* Traverser::shortestPath (VertexId const& start, + VertexId const& target) { + // For the result: std::deque r_vertices; std::deque r_edges; - highscore = 1e50; - bingo = false; + _highscoreSet = false; + _highscore = 0; + _bingo = false; // Forward with initialization: - _forwardLookup.clear(); - _forwardLookup.emplace(start, LookupInfo(0, "", "")); - _forwardQueue.clear(); - _forwardQueue.insert(QueueInfo(start, 0)); - ThreadInfo forwardInfo(_forwardLookup, _forwardQueue, _forwardMutex); + string empty; + ThreadInfo forward; + forward._pq.insert(start, Step(start, empty, 0, empty)); - _backwardLookup.clear(); - _backwardLookup.emplace(target, LookupInfo(0, "", "")); - _backwardQueue.clear(); - _backwardQueue.insert(QueueInfo(target, 0)); - ThreadInfo backwardInfo(_backwardLookup, _backwardQueue, _backwardMutex); + // backward with initialization: + ThreadInfo backward; + backward._pq.insert(target, Step(target, empty, 0, empty)); - Searcher forwardSearcher(this, forwardInfo, backwardInfo, start, - _forwardExpander, "X"); - Searcher backwardSearcher(this, backwardInfo, forwardInfo, target, - _backwardExpander, "Y"); + // Now the searcher threads: + Searcher forwardSearcher(this, forward, backward, start, + _forwardExpander, "Forward"); + Searcher backwardSearcher(this, backward, forward, target, + _backwardExpander, "Backward"); forwardSearcher.start(); backwardSearcher.start(); forwardSearcher.join(); backwardSearcher.join(); - if (!bingo || intermediate == "") { + if (!_bingo || _intermediate == "") { return nullptr; } - auto pathLookup = _forwardLookup.find(intermediate); + Step* s = forward._pq.lookup(_intermediate); + r_vertices.push_back(_intermediate); + // FORWARD Go path back from intermediate -> start. // Insert all vertices and edges at front of vector // Do NOT! insert the intermediate vertex - TRI_ASSERT(pathLookup != _forwardLookup.end()); - r_vertices.push_back(intermediate); - while (pathLookup->second.predecessor != "") { - r_edges.push_front(pathLookup->second.edge); - r_vertices.push_front(pathLookup->second.predecessor); - pathLookup = _forwardLookup.find(pathLookup->second.predecessor); + while (s->_predecessor != "") { + r_edges.push_front(s->_edge); + r_vertices.push_front(s->_predecessor); + s = forward._pq.lookup(s->_predecessor); } // BACKWARD Go path back from intermediate -> target. // Insert all vertices and edges at back of vector // Also insert the intermediate vertex - pathLookup = _backwardLookup.find(intermediate); - TRI_ASSERT(pathLookup != _backwardLookup.end()); - while (pathLookup->second.predecessor != "") { - r_edges.push_back(pathLookup->second.edge); - r_vertices.push_back(pathLookup->second.predecessor); - pathLookup = _backwardLookup.find(pathLookup->second.predecessor); + s = backward._pq.lookup(_intermediate); + while (s->_predecessor != "") { + r_edges.push_back(s->_edge); + r_vertices.push_back(s->_predecessor); + s = backward._pq.lookup(s->_predecessor); } - Path* res = new Path(r_vertices, r_edges, highscore); - return res; + return new Path(r_vertices, r_edges, _highscore); }; + diff --git a/lib/Basics/Traverser.h b/lib/Basics/Traverser.h index 88f9e0d6be..3011180976 100644 --- a/lib/Basics/Traverser.h +++ b/lib/Basics/Traverser.h @@ -34,8 +34,6 @@ #include -class Searcher; - namespace triagens { namespace basics { @@ -158,21 +156,22 @@ namespace triagens { } //////////////////////////////////////////////////////////////////////////////// -/// @brief lookup +/// @brief lookup, note that the resulting pointer is only valid until the +/// the next modification of the data structure happens (insert or lowerWeight +/// or popMinimal). The weight in the Value type must not be modified other +/// than via lowerWeight, otherwise the queue order could be violated. //////////////////////////////////////////////////////////////////////////////// - Value const* lookup (Key const& k) { + Value* lookup (Key const& k) { auto it = _lookup.find(k); if (it == _lookup.end()) { return nullptr; } if (it->second >= 0) { // still in the queue - return const_cast - (&(_heap[static_cast(it->second) - _popped])); + return &(_heap[static_cast(it->second) - _popped]); } else { // already in the history - return const_cast - (&(_history[static_cast(-it->second) - 1])); + return &(_history[static_cast(-it->second) - 1]); } } @@ -201,19 +200,24 @@ namespace triagens { } //////////////////////////////////////////////////////////////////////////////// -/// @brief getMinimal +/// @brief getMinimal, note that the resulting pointer is only valid until the +/// the next modification of the data structure happens (insert or lowerWeight +/// or popMinimal). The weight in the Value type must not be modified other +/// than via lowerWeight, otherwise the queue order could be violated. //////////////////////////////////////////////////////////////////////////////// - Value const* getMinimal() { + Value* getMinimal() { if (_heap.empty()) { return nullptr; } - return const_cast(&(_heap[0])); + return &(_heap[0]); } //////////////////////////////////////////////////////////////////////////////// /// @brief popMinimal, returns true if something was returned and false /// if the structure is empty. Key and Value are stored in k and v. +/// If keepForLookup is true then the Value is kept for lookup in the +/// hash table but removed from the priority queue. //////////////////////////////////////////////////////////////////////////////// bool popMinimal (Key& k, Value& v, bool keepForLookup = false) { @@ -358,15 +362,6 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// void removeFromHeap (bool keepForLookup) { - if (_heap.size() == 1) { - _heap.clear(); - _popped = 0; - _lookup.clear(); - _isHeap = false; - _maxWeight = 0; - return; - } - auto it = _lookup.find(_heap[0].getKey()); TRI_ASSERT(it != _lookup.end()); if (keepForLookup) { @@ -377,6 +372,14 @@ namespace triagens { else { _lookup.erase(it); } + if (_heap.size() == 1) { + _heap.clear(); + _popped = 0; + _isHeap = false; + _maxWeight = 0; + return; + } + // Move one in front: _heap[0] = _heap.back(); _heap.pop_back(); it = _lookup.find(_heap[0].getKey()); @@ -438,23 +441,29 @@ namespace triagens { class Traverser { - friend class ::Searcher; - // ----------------------------------------------------------------------------- // --SECTION-- data structures // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- -// --SECTION-- path +// --SECTION-- types // ----------------------------------------------------------------------------- public: +//////////////////////////////////////////////////////////////////////////////// +/// @brief types for vertices, edges and weights +//////////////////////////////////////////////////////////////////////////////// + typedef std::string VertexId; typedef std::string EdgeId; typedef double EdgeWeight; +//////////////////////////////////////////////////////////////////////////////// +/// @brief Path, type for the result +//////////////////////////////////////////////////////////////////////////////// + // Convention vertices.size() -1 === edges.size() // path is vertices[0] , edges[0], vertices[1] etc. struct Path { @@ -462,29 +471,43 @@ namespace triagens { std::deque edges; EdgeWeight weight; - Path ( - std::deque vertices, - std::deque edges, - EdgeWeight weight - ) : vertices(vertices), - edges(edges), - weight(weight) { + Path (std::deque vertices, std::deque edges, + EdgeWeight weight) + : vertices(vertices), edges(edges), weight(weight) { }; }; - struct Neighbor { - VertexId neighbor; - EdgeId edge; - EdgeWeight weight; +//////////////////////////////////////////////////////////////////////////////// +/// @brief Step, one position with a predecessor and the edge +//////////////////////////////////////////////////////////////////////////////// - Neighbor ( - VertexId neighbor, - EdgeId edge, - EdgeWeight weight - ) : neighbor(neighbor), - edge(edge), - weight(weight) { - }; + struct Step { + VertexId _vertex; + VertexId _predecessor; + EdgeWeight _weight; + EdgeId _edge; + bool _done; + + Step () : _done(false) { + } + + Step (VertexId const& vert, VertexId const& pred, + EdgeWeight weig, EdgeId const& edge) + : _vertex(vert), _predecessor(pred), _weight(weig), _edge(edge), + _done(false) { + } + + EdgeWeight weight () const { + return _weight; + } + + void setWeight (EdgeWeight w) { + _weight = w; + } + + VertexId const& getKey () const { + return _vertex; + } }; //////////////////////////////////////////////////////////////////////////////// @@ -493,7 +516,11 @@ namespace triagens { typedef enum {FORWARD, BACKWARD} Direction; - typedef std::function& result)> +//////////////////////////////////////////////////////////////////////////////// +/// @brief callback to find neighbours +//////////////////////////////////////////////////////////////////////////////// + + typedef std::function& result)> ExpanderFunction; // ----------------------------------------------------------------------------- @@ -504,12 +531,12 @@ namespace triagens { /// @brief create the Traverser //////////////////////////////////////////////////////////////////////////////// - Traverser ( - ExpanderFunction forwardExpander, - ExpanderFunction backwardExpander - ) : highscore(1e50), - bingo(false), - intermediate(""), + Traverser (ExpanderFunction forwardExpander, + ExpanderFunction backwardExpander) + : _highscoreSet(false), + _highscore(0), + _bingo(false), + _intermediate(""), _forwardExpander(forwardExpander), _backwardExpander(backwardExpander) { }; @@ -517,16 +544,14 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// /// @brief destructor //////////////////////////////////////////////////////////////////////////////// + ~Traverser () { - // TODO: Implement!! }; // ----------------------------------------------------------------------------- // --SECTION-- public methods // ----------------------------------------------------------------------------- - public: - //////////////////////////////////////////////////////////////////////////////// /// @brief Find the shortest path between start and target. /// Only edges having the given direction are followed. @@ -535,91 +560,59 @@ namespace triagens { // Caller has to free the result // nullptr indicates there is no path - Path* ShortestPath ( + Path* shortestPath ( VertexId const& start, VertexId const& target ); // ----------------------------------------------------------------------------- -// --SECTION-- private methods +// --SECTION-- public data // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// -/// @brief Function to compute all neighbors of a given vertex +/// @brief lowest total weight for a complete path found //////////////////////////////////////////////////////////////////////////////// - private: + bool _highscoreSet; - std::atomic highscore; - std::atomic bingo; - std::mutex resultMutex; - VertexId intermediate; +//////////////////////////////////////////////////////////////////////////////// +/// @brief lowest total weight for a complete path found +//////////////////////////////////////////////////////////////////////////////// - struct LookupInfo { - EdgeWeight weight; - bool done; - EdgeId edge; - VertexId predecessor; + EdgeWeight _highscore; - LookupInfo ( - EdgeWeight weight, - EdgeId edge, - VertexId predecessor - ) : weight(weight), - done(false), - edge(edge), - predecessor(predecessor) { - }; - }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief _bingo, flag that indicates termination +//////////////////////////////////////////////////////////////////////////////// - struct QueueInfo { - EdgeWeight weight; - VertexId vertex; + std::atomic _bingo; - QueueInfo ( - VertexId vertex, - EdgeWeight weight - ) : weight(weight), - vertex(vertex) { - }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief _resultMutex, this is used to protect access to the result data +//////////////////////////////////////////////////////////////////////////////// - friend bool operator< (QueueInfo const& a, QueueInfo const& b) { - if (a.weight == b.weight) { - return a.vertex < b.vertex; - } - return a.weight < b.weight; - }; + std::mutex _resultMutex; - }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief _intermediate, one vertex on the shortest path found +//////////////////////////////////////////////////////////////////////////////// + + VertexId _intermediate; + +// ----------------------------------------------------------------------------- +// --SECTION-- private data +// ----------------------------------------------------------------------------- + + typedef triagens::basics::PriorityQueue + PQueue; - // TODO: Destructor?! struct ThreadInfo { - std::unordered_map& lookup; - std::set>& queue; - std::mutex& mutex; - - ThreadInfo ( - std::unordered_map& lookup, - std::set>& queue, - std::mutex& mutex - ) : lookup(lookup), - queue(queue), - mutex(mutex) { - }; + PQueue _pq; + std::mutex _mutex; }; - ExpanderFunction _forwardExpander; ExpanderFunction _backwardExpander; - - // ShortestPath will create these variables - std::unordered_map _forwardLookup; - std::set> _forwardQueue; - std::mutex _forwardMutex; - - std::unordered_map _backwardLookup; - std::set> _backwardQueue; - std::mutex _backwardMutex; }; } }