From b4367947739f033d85d621710723498d6b33b57f Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 3 Aug 2022 13:34:38 +0100 Subject: [PATCH] Add specialized GraphStreamUnordered GraphStreamUnordered used to be GraphStream>, 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. --- src/V3Graph.h | 10 +++--- src/V3GraphStream.h | 82 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 82 insertions(+), 10 deletions(-) diff --git a/src/V3Graph.h b/src/V3Graph.h index ae59fe4a4..08d5c0938 100644 --- a/src/V3Graph.h +++ b/src/V3Graph.h @@ -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(_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; } diff --git a/src/V3GraphStream.h b/src/V3GraphStream.h index 37d68ca31..5c53f0a0f 100644 --- a/src/V3GraphStream.h +++ b/src/V3GraphStream.h @@ -27,6 +27,7 @@ #include #include #include +#include //###################################################################### // 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>; +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 m_nextVertices; // List of ready vertices returned next + std::vector 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(graphp); + } else { + init(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(resultp); + } else { + return unblock(resultp); + } + } + +private: + template // + 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 // + 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