You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@buildstream.apache.org by ro...@apache.org on 2020/12/29 13:35:13 UTC

[buildstream] 01/03: element: Rewrite 'dependencies()' to be more optimal

This is an automated email from the ASF dual-hosted git repository.

root pushed a commit to branch bschubert/optimize-deps
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit 5201c2983e1fdb903027d657c63493b73f24f8a6
Author: Benjamin Schubert <be...@gmail.com>
AuthorDate: Fri Jul 12 18:05:50 2019 +0100

    element: Rewrite 'dependencies()' to be more optimal
    
    This reduces the number of comparison we need to make and reduces
    the runtime cost of this method by roughly 25%
---
 src/buildstream/element.py | 65 +++++++++++++++++++++++++---------------------
 1 file changed, 36 insertions(+), 29 deletions(-)

diff --git a/src/buildstream/element.py b/src/buildstream/element.py
index fe7d366..9239a2c 100644
--- a/src/buildstream/element.py
+++ b/src/buildstream/element.py
@@ -80,7 +80,6 @@ from collections import OrderedDict
 import contextlib
 from contextlib import contextmanager
 from functools import partial
-from itertools import chain
 import string
 from typing import cast, TYPE_CHECKING, Any, Dict, Iterator, List, Optional
 
@@ -468,46 +467,54 @@ class Element(Plugin):
             if scope in (Scope.RUN, Scope.ALL):
                 yield from self.__runtime_dependencies
         else:
+            def visit_run(element, visited):
+                visited.add(element._unique_id)
 
-            def visit(element, scope, visited):
-                if scope == Scope.ALL:
-                    visited[0].add(element._unique_id)
-                    visited[1].add(element._unique_id)
+                for dep in element.__runtime_dependencies:
+                    if dep._unique_id not in visited:
+                        yield from visit_run(dep, visited)
 
-                    for dep in chain(element.__build_dependencies, element.__runtime_dependencies):
-                        if dep._unique_id not in visited[0] and dep._unique_id not in visited[1]:
-                            yield from visit(dep, Scope.ALL, visited)
+                yield element
 
-                    yield element
-                elif scope == Scope.BUILD:
-                    visited[0].add(element._unique_id)
+            def visit_build(element, visited_build, visited_run):
+                visited_build.add(element._unique_id)
 
-                    for dep in element.__build_dependencies:
-                        if dep._unique_id not in visited[1]:
-                            yield from visit(dep, Scope.RUN, visited)
+                for dep in element.__build_dependencies:
+                    if dep._unique_id not in visited_run:
+                        yield from visit_run(dep, visited_run)
 
-                elif scope == Scope.RUN:
-                    visited[1].add(element._unique_id)
+            def visit_all(element, visited):
+                visited.add(element._unique_id)
 
-                    for dep in element.__runtime_dependencies:
-                        if dep._unique_id not in visited[1]:
-                            yield from visit(dep, Scope.RUN, visited)
+                for dep in element.__build_dependencies:
+                    if dep._unique_id not in visited:
+                        yield from visit_all(dep, visited)
 
-                    yield element
-                else:
-                    yield element
+                for dep in element.__runtime_dependencies:
+                    if dep._unique_id not in visited:
+                        yield from visit_all(dep, visited)
+
+                yield element
 
             if visited is None:
                 # Visited is of the form (Visited for Scope.BUILD, Visited for Scope.RUN)
                 visited = (BitMap(), BitMap())
-            else:
-                # We have already a visited set passed. we might be able to short-circuit
-                if scope in (Scope.BUILD, Scope.ALL) and self._unique_id in visited[0]:
-                    return
-                if scope in (Scope.RUN, Scope.ALL) and self._unique_id in visited[1]:
-                    return
 
-            yield from visit(self, scope, visited)
+            if scope == Scope.ALL:
+                # We can use only one of the sets when checking for Scope.ALL, as we would get added to
+                # both anyways.
+                # This would break if we start reusing 'visited' and mixing scopes, but that is done
+                # nowhere in the codebase.
+                if self._unique_id not in visited[0]:
+                    yield from visit_all(self, visited[0])
+            elif scope == Scope.BUILD:
+                if self._unique_id not in visited[0]:
+                    yield from visit_build(self, visited[0], visited[1])
+            elif scope == Scope.RUN:
+                if self._unique_id not in visited[1]:
+                    yield from visit_run(self, visited[1])
+            else:
+                yield self
 
     def search(self, scope: Scope, name: str) -> Optional["Element"]:
         """Search for a dependency by name