[Pkg-puppet-devel] [SCM] Puppet packaging for Debian branch, experimental, updated. debian/2.6.8-1-844-g7ec39d5

Daniel Pittman daniel at rimspace.net
Tue May 10 08:04:21 UTC 2011


The following commit has been merged in the experimental branch:
commit f547118462cac124dc826ad96d00574711f1d3cd
Author: Daniel Pittman <daniel at rimspace.net>
Date:   Sun Jan 23 00:05:11 2011 -0800

    Feature #2597 -- use iterative DFS in Tarjan clustering.
    
    In order to bypass the limitations of the C stack, which is also the Ruby
    stack, we replace the simple and clear recursive Trajan implementation with an
    iterative version that uses the heap as the stack.
    
    This is somewhat harder to read, but can now run a 10,000 vertex deep linear
    dependency relationship where, previously, 1,250 was about the limit on my
    machine.
    
    This should now be bounded by the size of the heap rather than the stack on
    all platforms -- though it would be nice to get rid of the magic and return to
    the recursive version if Ruby ever follows Perl down the sensible path of
    essentially unlimited recursion by writing that code for us in the
    interpreter...

diff --git a/lib/puppet/simple_graph.rb b/lib/puppet/simple_graph.rb
index b900793..064f6be 100644
--- a/lib/puppet/simple_graph.rb
+++ b/lib/puppet/simple_graph.rb
@@ -101,42 +101,66 @@ class Puppet::SimpleGraph
   # This method has an unhealthy relationship with the find_cycles_in_graph
   # method below, which contains the knowledge of how the state object is
   # maintained.
-  def tarjan(v, s)
-    s.index[v]   = s.n
-    s.lowlink[v] = s.n
-    s.n = s.n + 1
-
-    s.s.push v
-
-    @out_from[v].each do |edge|
-      to = edge[0]
-
-      if ! s.index[to] then
-        tarjan(to, s)
-        s.lowlink[v] = [s.lowlink[v], s.lowlink[to]].min
-      elsif s.s.member? to then
-        # Performance note: the stack membership test *should* be done with a
-        # constant time check, but I was lazy and used something that is
-        # likely to be O(N) where N is the stack depth; this will bite us
-        # eventually, and should be improved before the change lands.
-        #
-        # OTOH, this is only invoked on a very cold path, when things have
-        # gone wrong anyhow, right now.  I feel that getting the code out is
-        # worth more than that final performance boost. --daniel 2011-01-22
-        s.lowlink[v] = [s.lowlink[v], s.index[to]].min
-      end
-    end
+  def tarjan(root, s)
+    # initialize the recursion stack we use to work around the nasty lack of a
+    # decent Ruby stack.
+    recur = [OpenStruct.new :node => root]
+
+    while not recur.empty? do
+      frame = recur.last
+      v = frame.node
+      case frame.step
+      when nil then
+        s.index[v]   = s.n
+        s.lowlink[v] = s.n
+        s.n          = s.n + 1
+
+        s.s.push v
+
+        frame.children = adjacent(v)
+        frame.step = :children
+
+      when :children then
+        if frame.children.length > 0 then
+          child = frame.children.shift
+          if ! s.index[child] then
+            # Never seen, need to recurse.
+            frame.step = :after_recursion
+            frame.child = child
+            recur.push OpenStruct.new :node => child
+          elsif s.s.member? child then
+            # Performance note: the stack membership test *should* be done with a
+            # constant time check, but I was lazy and used something that is
+            # likely to be O(N) where N is the stack depth; this will bite us
+            # eventually, and should be improved before the change lands.
+            #
+            # OTOH, this is only invoked on a very cold path, when things have
+            # gone wrong anyhow, right now.  I feel that getting the code out is
+            # worth more than that final performance boost. --daniel 2011-01-22
+            s.lowlink[v] = [s.lowlink[v], s.index[child]].min
+          end
+        else
+          if s.lowlink[v] == s.index[v] then
+            # REVISIT: Surely there must be a nicer way to partition this around an
+            # index, but I don't know what it is.  This works. :/ --daniel 2011-01-22
+            #
+            # Performance note: this might also suffer an O(stack depth) performance
+            # hit, better replaced with something that is O(1) for splitting the
+            # stack into parts.
+            tmp = s.s.slice!(0, s.s.index(v))
+            s.scc.push s.s
+            s.s = tmp
+          end
+          recur.pop               # done with this node, finally.
+        end
+
+      when :after_recursion then
+        s.lowlink[v] = [s.lowlink[v], s.lowlink[frame.child]].min
+        frame.step = :children
 
-    if s.lowlink[v] == s.index[v] then
-      # REVISIT: Surely there must be a nicer way to partition this around an
-      # index, but I don't know what it is.  This works. :/ --daniel 2011-01-22
-      #
-      # Performance note: this might also suffer an O(stack depth) performance
-      # hit, better replaced with something that is O(1) for splitting the
-      # stack into parts.
-      tmp = s.s.slice!(0, s.s.index(v))
-      s.scc.push s.s
-      s.s = tmp
+      else
+        fail "#{frame.step} is an unknown step"
+      end
     end
   end
 
diff --git a/spec/unit/simple_graph_spec.rb b/spec/unit/simple_graph_spec.rb
index a8e0c44..3b0fe9a 100755
--- a/spec/unit/simple_graph_spec.rb
+++ b/spec/unit/simple_graph_spec.rb
@@ -350,7 +350,7 @@ describe Puppet::SimpleGraph do
     end
 
     it "cycle discovery should not fail with large data sets" do
-      limit = 1000
+      limit = 3000
       (1..(limit - 1)).each do |n| add_edges n.to_s => (n+1).to_s end
 
       cycles = nil

-- 
Puppet packaging for Debian



More information about the Pkg-puppet-devel mailing list