From 13f08e82e62559037e406dba6770895868df40c4 Mon Sep 17 00:00:00 2001 From: Steven Knight Date: Thu, 16 Dec 2004 18:45:45 +0000 Subject: Speed up Taskmaster by not calling Node methods so frequently. --- src/CHANGES.txt | 12 ++++-- src/engine/SCons/Node/__init__.py | 6 +-- src/engine/SCons/Taskmaster.py | 78 +++++++++++++++------------------------ 3 files changed, 39 insertions(+), 57 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 55e74ff..c59f61f 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -331,10 +331,14 @@ RELEASE 0.97 - XXX - Fix command-line expansion of Python Value Nodes. - Internal cleanups: Remove an unnecessary scan argument. Associate - Scanners only with Builders, not nodes. Apply overrides once when a - Builder is called, not in multiple places. Cache results from - the Node.FS.get_suffix() and Node.get_build_env() methods. Use - the Python md5 modules' hexdigest() method, if there is one. + Scanners only with Builders, not nodes. Apply overrides once when + a Builder is called, not in multiple places. Cache results from the + Node.FS.get_suffix() and Node.get_build_env() methods. Use the Python + md5 modules' hexdigest() method, if there is one. Have Taskmaster + call get_stat() once for each Node and re-use the value instead of + calling it each time it needs the value. Have Node.depends_on() + re-use the list from the children() method instead of calling it + multiple times. - Use the correct scanner if the same source file is used for targets in two different environments with the same path but different scanners. diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index abbdf87..f0e750b 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -296,11 +296,7 @@ class Node: def depends_on(self, nodes): """Does this node depend on any of 'nodes'?""" - for node in nodes: - if node in self.children(): - return 1 - - return 0 + return reduce(lambda D,N,C=self.children(): D or (N in C), nodes, 0) def builder_set(self, builder): self.builder = builder diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py index b528f44..404ded1 100644 --- a/src/engine/SCons/Taskmaster.py +++ b/src/engine/SCons/Taskmaster.py @@ -261,7 +261,15 @@ class Taskmaster: self.ready = None # the next task that is ready to be executed self.order = order self.message = None - self.altered = [] + + # See if we can alter the target list to find any + # corresponding targets in linked build directories + for node in self.targets: + alt, message = node.alter_targets() + if alt: + self.message = message + self.candidates.extend(self.order(alt)) + continue def _find_next_ready_node(self): """Find the next node that is ready to be built""" @@ -284,7 +292,9 @@ class Taskmaster: node.set_state(SCons.Node.stack) try: - children = node.children() + childinfo = map(lambda N: (N.get_state(), + N.is_derived() or N.is_pseudo_derived(), + N), node.children()) except SystemExit: exc_value = sys.exc_info()[1] e = SCons.Errors.ExplicitExit(node, exc_value.code) @@ -304,79 +314,51 @@ class Taskmaster: self.ready = node break + # Skip this node if any of its children have failed. This # catches the case where we're descending a top-level target # and one of our children failed while trying to be built # by a *previous* descent of an earlier top-level target. - def failed(node): return node.get_state() == SCons.Node.failed - if filter(failed, children): + if filter(lambda I: I[0] == SCons.Node.failed, childinfo): node.set_state(SCons.Node.failed) self.candidates.pop() continue # Detect dependency cycles: - def in_stack(node): return node.get_state() == SCons.Node.stack - cycle = filter(in_stack, children) + cycle = filter(lambda I: I[0] == SCons.Node.stack, childinfo) if cycle: - nodes = filter(in_stack, self.candidates) + cycle + nodes = filter(lambda N: N.get_state() == SCons.Node.stack, + self.candidates) + \ + map(lambda I: I[2], cycle) nodes.reverse() desc = "Dependency cycle: " + string.join(map(str, nodes), " -> ") raise SCons.Errors.UserError, desc # Find all of the derived dependencies (that is, # children who have builders or are side effects): - try: - def derived_nodes(node): return node.is_derived() or node.is_pseudo_derived() - derived = filter(derived_nodes, children) - except KeyboardInterrupt: - raise - except: - # We had a problem just trying to figure out if any of - # the kids are derived (like a child couldn't be linked - # from a repository). Arrange to raise the exception - # when the Task is "executed." - self.ready_exc = sys.exc_info() - self.candidates.pop() - self.ready = node - break - - # If this was a top-level argument and we haven't already - # done so, see if we can alter the target list to find any - # corresponding targets in linked build directories: - if node in self.targets and node not in self.altered: - alt, message = node.alter_targets() - if alt: - self.message = message - self.candidates.extend(self.order(alt)) - self.altered.append(node) - continue - # Add derived files that have not been built # to the candidates list: - def unbuilt_nodes(node): return node.get_state() == None - not_built = filter(unbuilt_nodes, derived) + not_built = filter(lambda I: I[1] and not I[0], childinfo) if not_built: # We're waiting on one more derived files that have not # yet been built. Add this node to the waiting_parents # list of each of those derived files. - def add_to_waiting_parents(child, parent=node): - child.add_to_waiting_parents(parent) - map(add_to_waiting_parents, not_built) + map(lambda I, P=node: I[2].add_to_waiting_parents(P), not_built) not_built.reverse() - self.candidates.extend(self.order(not_built)) + self.candidates.extend(self.order(map(lambda I: I[2], + not_built))) continue # Skip this node if it has side-effects that are # currently being built: - cont = 0 - for side_effect in node.side_effects: - if side_effect.get_state() == SCons.Node.executing: - self.pending.append(node) - node.set_state(SCons.Node.pending) - self.candidates.pop() - cont = 1 - break - if cont: continue + if reduce(lambda E,N: + E or N.get_state() == SCons.Node.executing, + node.side_effects, + 0): + self.pending.append(node) + node.set_state(SCons.Node.pending) + self.candidates.pop() + continue # Skip this node if it is pending on a currently # executing node: -- cgit v0.12