Eliminate redundant signature calculations, optimize out use of hasattr().
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 17 May 2003 20:45:44 +0000 (20:45 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 17 May 2003 20:45:44 +0000 (20:45 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@687 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/CHANGES.txt
src/engine/SCons/Node/FS.py
src/engine/SCons/Node/__init__.py
src/engine/SCons/Sig/MD5.py

index 6c84af6bb897bcdb01d14d913021cba2d23a38d7..df7774946be2addf6d3e64b3f87a9db791025171 100644 (file)
@@ -86,6 +86,10 @@ RELEASE 0.14 - XXX
   - Refactor the internal representation of a single execution instance
     of an action to eliminate redundant signature calculations.
 
+  - Eliminate redundant signature calculations for Nodes.
+
+  - Optimize out calling hasattr() before accessing attributes.
+
   From Damyan Pepper:
 
   - Quote the "Entering directory" message like Make.
index ff4dfb5cfee5893672e912663097d5ff3da7aa37..c226504c9dbf01001053eabc13685ab94f3394bc 100644 (file)
@@ -378,9 +378,11 @@ class Entry(SCons.Node.Node):
             return self._exists
 
     def rexists(self):
-        if not hasattr(self, '_rexists'):
+        try:
+            return self._rexists
+        except AttributeError:
             self._rexists = self.rfile().exists()
-        return self._rexists
+            return self._rexists
 
     def get_parents(self):
         parents = SCons.Node.Node.get_parents(self)
@@ -1112,25 +1114,28 @@ class File(Entry):
         else:
             return 0
 
-    def calc_signature(self, calc, cache=None):
+    def calc_signature(self, calc):
         """
         Select and calculate the appropriate build signature for a File.
 
         self - the File node
         calc - the signature calculation module
-        cache - alternate node to use for the signature cache
         returns - the signature
         """
-
-        if self.is_derived():
-            if SCons.Sig.build_signature:
-                return calc.bsig(self.rfile(), self)
+        try:
+            return self._calculated_sig
+        except AttributeError:
+            if self.is_derived():
+                if SCons.Sig.build_signature:
+                    sig = self.rfile().calc_bsig(calc, self)
+                else:
+                    sig = self.rfile().calc_csig(calc, self)
+            elif not self.rexists():
+                sig = None
             else:
-                return calc.csig(self.rfile(), self)
-        elif not self.rexists():
-            return None
-        else:
-            return calc.csig(self.rfile(), self)
+                sig = self.rfile().calc_csig(calc, self)
+            self._calculated_sig = sig
+            return sig
         
     def store_csig(self):
         self.dir.sconsign().set_csig(self.name, self.get_csig())
@@ -1208,10 +1213,14 @@ class File(Entry):
                 # created the directory, depending on whether the -n
                 # option was used or not.  Delete the _exists and
                 # _rexists attributes so they can be reevaluated.
-                if hasattr(dirnode, '_exists'):
+                try:
                     delattr(dirnode, '_exists')
-                if hasattr(dirnode, '_rexists'):
+                except AttributeError:
+                    pass
+                try:
                     delattr(dirnode, '_rexists')
+                except AttributeError:
+                    pass
             except OSError:
                 pass
 
@@ -1253,10 +1262,14 @@ class File(Entry):
             CachePush(self, None, None)
         SCons.Node.Node.built(self)
         self.found_includes = {}
-        if hasattr(self, '_exists'):
+        try:
             delattr(self, '_exists')
-        if hasattr(self, '_rexists'):
+        except AttributeError:
+            pass
+        try:
             delattr(self, '_rexists')
+        except AttributeError:
+            pass
 
     def visited(self):
         if self.fs.CachePath and self.fs.cache_force and os.path.exists(self.path):
@@ -1321,8 +1334,10 @@ class File(Entry):
                     except OSError, e:
                         raise SCons.Errors.BuildError(node = self,
                                                       errstr = e.strerror)
-                    if hasattr(self, '_exists'):
+                    try:
                         delattr(self, '_exists')
+                    except AttributeError:
+                        pass
             else:
                 try:
                     self._createDir()
@@ -1357,10 +1372,14 @@ class File(Entry):
                 # created the file, depending on whether the -n
                 # option was used or not.  Delete the _exists and
                 # _rexists attributes so they can be reevaluated.
-                if hasattr(self, '_exists'):
+                try:
                     delattr(self, '_exists')
-                if hasattr(self, '_rexists'):
+                except AttributeError:
+                    pass
+                try:
                     delattr(self, '_rexists')
+                except AttributeError:
+                    pass
         return Entry.exists(self)
 
     def current(self, calc):
@@ -1384,14 +1403,16 @@ class File(Entry):
             return calc.current(self, bsig)
 
     def rfile(self):
-        if not hasattr(self, '_rfile'):
+        try:
+            return self._rfile
+        except:
             self._rfile = self
             if not self.exists():
                 n = self.fs.Rsearch(self.path, clazz=File,
                                     cwd=self.fs.Top)
                 if n:
                     self._rfile = n
-        return self._rfile
+            return self._rfile
 
     def rstr(self):
         return str(self.rfile())
index f2d77ceb054b8f043bd44f0be96d1c05deca189f..43ae7c5c26fc31ad293af6083b905d3067f4fffb 100644 (file)
@@ -209,6 +209,14 @@ class Node:
         self.set_state(None)
         self.del_bsig()
         self.del_csig()
+        try:
+            delattr(self, '_calculated_sig')
+        except AttributeError:
+            pass
+        try:
+            delattr(self, '_tempbsig')
+        except AttributeError:
+            pass
         self.includes = None
         self.found_includes = {}
         self.implicit = None
@@ -383,37 +391,63 @@ class Node:
             return
         self.env = env
 
-    def calc_signature(self, calc, cache=None):
+    def calc_signature(self, calc):
         """
         Select and calculate the appropriate build signature for a node.
 
         self - the node
         calc - the signature calculation module
-        cache - alternate node to use for the signature cache
         returns - the signature
         """
-
-        if self.has_builder():
-            if SCons.Sig.build_signature:
-                return calc.bsig(self, cache)
+        try:
+            return self._calculated_sig
+        except AttributeError:
+            if self.has_builder():
+                if SCons.Sig.build_signature:
+                    sig = self.calc_bsig(calc)
+                else:
+                    sig = self.calc_csig(calc)
+            elif not self.exists():
+                sig = None
             else:
-                return calc.csig(self, cache)
-        elif not self.exists():
-            return None
-        else:
-            return calc.csig(self, cache)
+                sig = self.calc_csig(calc)
+            self._calculated_sig = sig
+            return sig
+
+    def calc_bsig(self, calc, cache=None):
+        """Return the node's build signature, calculating it first
+        if necessary.
+
+        Note that we don't save it in the "real" build signature
+        attribute if we have to calculate it here; the "real" build
+        signature only gets updated after a file is actually built.
+        """
+        if cache is None: cache = self
+        try:
+            return cache.bsig
+        except AttributeError:
+            try:
+                return cache._tempbsig
+            except AttributeError:
+                cache._tempbsig = calc.bsig(self, cache)
+                return cache._tempbsig
 
     def get_bsig(self):
         """Get the node's build signature (based on the signatures
         of its dependency files and build information)."""
-        if not hasattr(self, 'bsig'):
+        try:
+            return self.bsig
+        except AttributeError:
             return None
-        return self.bsig
 
     def set_bsig(self, bsig):
         """Set the node's build signature (based on the signatures
         of its dependency files and build information)."""
         self.bsig = bsig
+        try:
+            delattr(self, '_tempbsig')
+        except AttributeError:
+            pass
 
     def store_bsig(self):
         """Make the build signature permanent (that is, store it in the
@@ -422,14 +456,28 @@ class Node:
 
     def del_bsig(self):
         """Delete the bsig from this node."""
-        if hasattr(self, 'bsig'):
+        try:
             delattr(self, 'bsig')
+        except AttributeError:
+            pass
 
     def get_csig(self):
         """Get the signature of the node's content."""
-        if not hasattr(self, 'csig'):
+        try:
+            return self.csig
+        except AttributeError:
             return None
-        return self.csig
+
+    def calc_csig(self, calc, cache=None):
+        """Return the node's content signature, calculating it first
+        if necessary.
+        """
+        if cache is None: cache = self
+        try:
+            return cache.csig
+        except AttributeError:
+            cache.csig = calc.csig(self, cache)
+            return cache.csig
 
     def set_csig(self, csig):
         """Set the signature of the node's content."""
@@ -442,8 +490,10 @@ class Node:
 
     def del_csig(self):
         """Delete the csig from this node."""
-        if hasattr(self, 'csig'):
+        try:
             delattr(self, 'csig')
+        except AttributeError:
+            pass
 
     def get_prevsiginfo(self):
         """Fetch the previous signature information from the
index 5239dab2f44c2cb5ac3ca0cb17bdbe517fa5deec..c50dbb7f416b784886ef43ef93db56aa6d084617 100644 (file)
@@ -78,9 +78,11 @@ def collect(signatures):
 def signature(obj):
     """Generate a signature for an object
     """
-    if not hasattr(obj, 'get_contents'):
+    try:
+        contents = str(obj.get_contents())
+    except AttributeError:
         raise AttributeError, "unable to fetch contents of '%s'" % str(obj)
-    return hexdigest(md5.new(str(obj.get_contents())).digest())
+    return hexdigest(md5.new(contents).digest())
 
 def to_string(signature):
     """Convert a signature to a string"""