Omit fake edge to root

This saves singificant time in comparison

Signed-off-by: Todd Gamblin <tgamblin@llnl.gov>
This commit is contained in:
Todd Gamblin 2025-03-31 17:20:14 -07:00
parent 8363fbf40f
commit 485291ef20
No known key found for this signature in database
GPG Key ID: C16729F1AACF66C6
2 changed files with 18 additions and 28 deletions

View File

@ -3815,8 +3815,7 @@ def _cmp_iter(self):
# 2. A generator over canonical edges
#
# Canonical edges have consistent ids defined by breadth-first traversal order. That is,
# the fake edge from nothing to the root is (0, 1), the root is always 1, dependencies of
# the root are 2, 3, 4, 5, etc., and so on.
# the root is always 0, dependencies of the root are 1, 2, 3, etc., and so on.
#
# The three cases are:
#
@ -3839,19 +3838,14 @@ def _cmp_iter(self):
# need for the complexity here. It was not clear at the time of writing that how
# much optimization was possible in `spack.traverse`.
def yield_root_edge():
yield 0 # id of None (parent of root)
yield 1 # id of root
yield 0 # depflag on fake edge
yield () # virtuals on fake edge
if not self._dependencies:
# Spec has no dependencies -- simplest case
def nodes():
yield self._cmp_node # just yield this node
def edges():
yield yield_root_edge
return
yield
elif not any(
dep.spec._dependencies for deplist in self._dependencies.values() for dep in deplist
@ -3879,15 +3873,12 @@ def nodes():
yield edge.spec._cmp_node
def edges():
# yield root edge consistent with what traverse would do.
yield yield_root_edge
# we've already sorted to yield dependency nodes in order, now yield
# edges for comparison in the same order, with BFS-consistent ids
for i, edge in enumerate(sorted_edges, start=2):
for i, edge in enumerate(sorted_edges, start=1):
def yield_edge(i=i, edge=edge):
yield 1 # id of root
yield 0 # id of root
yield i # id of dependency in BFS order
yield edge.depflag
yield edge.virtuals
@ -3900,7 +3891,6 @@ def yield_edge(i=i, edge=edge):
# We need a way to compare edges consistently between two arbitrary specs.
# node_ids generates consistent node ids based on BFS traversal order.
node_ids = collections.defaultdict(lambda: len(node_ids))
node_ids[id(None)] # None (parent of root) is always node id 0
# To avoid traversing twice, we put edges in this list as we yield the nodes.
# edges() yields the edge functions later, if necessary.
@ -3915,6 +3905,10 @@ def nodes():
yield edge.spec._cmp_node
node_ids[id(edge.spec)]
# skip fake edge to root
if edge.parent is None:
continue
# create "edges" with the ids we generated above
def yield_edge(edge=edge):
yield node_ids[id(edge.parent)]

View File

@ -2057,10 +2057,7 @@ def test_spec_ordering(specs_in_expected_order):
"spec,expected_tuplified",
[
# simple, no dependencies
[
("a"),
((("a", None, EMPTY_VER, EMPTY_VAR, EMPTY_FLG, None, None, None),), ((0, 1, 0, ()),)),
],
[("a"), ((("a", None, EMPTY_VER, EMPTY_VAR, EMPTY_FLG, None, None, None),), ())],
# with some node attributes
[
("a@1.0 +foo cflags='-O3 -g'"),
@ -2077,7 +2074,7 @@ def test_spec_ordering(specs_in_expected_order):
None,
),
),
((0, 1, 0, ()),),
(),
),
],
# single edge case
@ -2088,7 +2085,7 @@ def test_spec_ordering(specs_in_expected_order):
("a", None, EMPTY_VER, EMPTY_VAR, EMPTY_FLG, None, None, None),
("b", None, EMPTY_VER, EMPTY_VAR, EMPTY_FLG, None, None, None),
),
((0, 1, 0, ()), (1, 2, 0, ())),
((0, 1, 0, ()),),
),
],
# root with multiple deps
@ -2101,7 +2098,7 @@ def test_spec_ordering(specs_in_expected_order):
("c", None, EMPTY_VER, EMPTY_VAR, EMPTY_FLG, None, None, None),
("d", None, EMPTY_VER, EMPTY_VAR, EMPTY_FLG, None, None, None),
),
((0, 1, 0, ()), (1, 2, 0, ()), (1, 3, 0, ()), (1, 4, 0, ())),
((0, 1, 0, ()), (0, 2, 0, ()), (0, 3, 0, ())),
),
],
# root with multiple build deps
@ -2114,7 +2111,7 @@ def test_spec_ordering(specs_in_expected_order):
("c", None, EMPTY_VER, EMPTY_VAR, EMPTY_FLG, None, None, None),
("d", None, EMPTY_VER, EMPTY_VAR, EMPTY_FLG, None, None, None),
),
((0, 1, 0, ()), (1, 2, dt.BUILD, ()), (1, 3, dt.BUILD, ()), (1, 4, dt.BUILD, ())),
((0, 1, dt.BUILD, ()), (0, 2, dt.BUILD, ()), (0, 3, dt.BUILD, ())),
),
],
# dependencies with dependencies
@ -2132,12 +2129,11 @@ def test_spec_ordering(specs_in_expected_order):
),
(
(0, 1, 0, ()),
(1, 2, 0, ()),
(1, 3, 0, ()),
(2, 4, dt.BUILD, ()),
(0, 2, 0, ()),
(1, 3, dt.BUILD, ()),
(1, 4, dt.BUILD, ()),
(2, 5, dt.BUILD, ()),
(3, 6, dt.BUILD, ()),
(3, 7, dt.BUILD, ()),
(2, 6, dt.BUILD, ()),
),
),
],