Add specialized GraphStreamUnordered
GraphStreamUnordered used to be GraphStream<std::less<const V3GraphVertex*>>, but a lot of performance improvements can be had by a specialized implementation, so added a highly optimized one. This helps a lot with --debug-partition.
This commit is contained in:
parent
90d22cbec6
commit
b436794773
|
|
@ -57,9 +57,9 @@ public:
|
|||
inline GraphWay()
|
||||
: m_e{FORWARD} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline GraphWay(en _e)
|
||||
inline constexpr GraphWay(en _e)
|
||||
: m_e{_e} {}
|
||||
explicit inline GraphWay(int _e)
|
||||
explicit inline constexpr GraphWay(int _e)
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
operator en() const { return m_e; }
|
||||
const char* ascii() const {
|
||||
|
|
@ -67,9 +67,9 @@ public:
|
|||
return names[m_e];
|
||||
}
|
||||
// METHODS unique to this class
|
||||
GraphWay invert() const { return m_e == FORWARD ? REVERSE : FORWARD; }
|
||||
bool forward() const { return m_e == FORWARD; }
|
||||
bool reverse() const { return m_e != FORWARD; }
|
||||
constexpr GraphWay invert() const { return m_e == FORWARD ? REVERSE : FORWARD; }
|
||||
constexpr bool forward() const { return m_e == FORWARD; }
|
||||
constexpr bool reverse() const { return m_e != FORWARD; }
|
||||
};
|
||||
inline bool operator==(const GraphWay& lhs, const GraphWay& rhs) { return lhs.m_e == rhs.m_e; }
|
||||
inline bool operator==(const GraphWay& lhs, GraphWay::en rhs) { return lhs.m_e == rhs; }
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
//######################################################################
|
||||
// GraphStream
|
||||
|
|
@ -225,11 +226,82 @@ private:
|
|||
VL_UNCOPYABLE(GraphStream);
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
//=================================================================================================
|
||||
// GraphStreamUnordered is similar to GraphStream, but iterates un-ordered vertices (those that are
|
||||
// not ordered by dependencies) in an arbitrary order. Iteration order is still deterministic.
|
||||
|
||||
// GraphStreamUnordered is GraphStream using a plain pointer compare to
|
||||
// break ties in the graph order. This WILL return nodes in
|
||||
// nondeterministic order.
|
||||
using GraphStreamUnordered = GraphStream<std::less<const V3GraphVertex*>>;
|
||||
class GraphStreamUnordered final {
|
||||
// MEMBERS
|
||||
const GraphWay m_way; // Direction of traversal
|
||||
size_t m_nextIndex = 0; // Which index to return from m_nextVertices next
|
||||
std::vector<const V3GraphVertex*> m_nextVertices; // List of ready vertices returned next
|
||||
std::vector<const V3GraphVertex*> m_readyVertices; // List of other ready vertices
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(GraphStreamUnordered);
|
||||
explicit GraphStreamUnordered(const V3Graph* graphp, GraphWay way = GraphWay::FORWARD)
|
||||
: m_way{way} {
|
||||
if (m_way == GraphWay::FORWARD) {
|
||||
init<GraphWay::FORWARD>(graphp);
|
||||
} else {
|
||||
init<GraphWay::REVERSE>(graphp);
|
||||
}
|
||||
}
|
||||
~GraphStreamUnordered() = default;
|
||||
|
||||
// METHODS
|
||||
|
||||
// Each call to nextp() returns a unique vertex in the graph, in dependency order. Dependencies
|
||||
// alone do not specify a total ordering. Un-ordered vertices are returned in an arbitrary but
|
||||
// deterministic order.
|
||||
const V3GraphVertex* nextp() {
|
||||
if (VL_UNLIKELY(m_nextIndex == m_nextVertices.size())) {
|
||||
if (VL_UNLIKELY(m_readyVertices.empty())) return nullptr;
|
||||
m_nextIndex = 0;
|
||||
// Use swap to avoid reallocation
|
||||
m_nextVertices.swap(m_readyVertices);
|
||||
m_readyVertices.clear();
|
||||
}
|
||||
const V3GraphVertex* const resultp = m_nextVertices[m_nextIndex++];
|
||||
if (m_way == GraphWay::FORWARD) {
|
||||
return unblock<GraphWay::FORWARD>(resultp);
|
||||
} else {
|
||||
return unblock<GraphWay::REVERSE>(resultp);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <uint8_t T_Way> //
|
||||
VL_ATTR_NOINLINE void init(const V3Graph* graphp) {
|
||||
constexpr GraphWay way{T_Way};
|
||||
constexpr GraphWay inv = way.invert();
|
||||
// Assign every vertex without an incoming edge to ready, others to waiting
|
||||
for (V3GraphVertex *vertexp = graphp->verticesBeginp(), *nextp; vertexp; vertexp = nextp) {
|
||||
nextp = vertexp->verticesNextp();
|
||||
uint32_t nDeps = 0;
|
||||
for (V3GraphEdge* edgep = vertexp->beginp(inv); edgep; edgep = edgep->nextp(inv)) {
|
||||
++nDeps;
|
||||
}
|
||||
vertexp->color(nDeps); // Using color instead of user, as user might be used by client
|
||||
if (VL_UNLIKELY(nDeps == 0)) m_nextVertices.push_back(vertexp);
|
||||
}
|
||||
}
|
||||
|
||||
template <uint8_t T_Way> //
|
||||
VL_ATTR_NOINLINE const V3GraphVertex* unblock(const V3GraphVertex* resultp) {
|
||||
constexpr GraphWay way{T_Way};
|
||||
for (V3GraphEdge *edgep = resultp->beginp(way), *nextp; edgep; edgep = nextp) {
|
||||
nextp = edgep->nextp(way);
|
||||
V3GraphVertex* const vertexp = edgep->furtherp(way);
|
||||
#if VL_DEBUG
|
||||
UASSERT_OBJ(vertexp->color() != 0, vertexp, "Should not be on waiting list");
|
||||
#endif
|
||||
vertexp->color(vertexp->color() - 1);
|
||||
if (!vertexp->color()) m_readyVertices.push_back(vertexp);
|
||||
}
|
||||
return resultp; // Returning input so we can tail call this method
|
||||
}
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
|
|||
Loading…
Reference in New Issue