[pypy-commit] pypy optresult: Add and use a helper module that delays recursive calls, to turn them

arigo noreply at buildbot.pypy.org
Fri Jun 5 10:51:24 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: optresult
Changeset: r77890:b3cb86b784a3
Date: 2015-06-05 10:36 +0200
http://bitbucket.org/pypy/pypy/changeset/b3cb86b784a3/

Log:	Add and use a helper module that delays recursive calls, to turn
	them non-recursive.

diff --git a/rpython/annotator/bookkeeper.py b/rpython/annotator/bookkeeper.py
--- a/rpython/annotator/bookkeeper.py
+++ b/rpython/annotator/bookkeeper.py
@@ -21,6 +21,7 @@
 from rpython.annotator.argument import simple_args
 from rpython.rlib.objectmodel import r_dict, r_ordereddict, Symbolic
 from rpython.tool.algo.unionfind import UnionFind
+from rpython.tool.flattenrec import FlattenRecursion
 from rpython.rtyper import extregistry
 
 
@@ -426,6 +427,8 @@
             self.methoddescs[key] = result
             return result
 
+    _see_mutable_flattenrec = FlattenRecursion()
+
     def see_mutable(self, x):
         key = (x.__class__, x)
         if key in self.seen_mutable:
@@ -434,8 +437,11 @@
         self.seen_mutable[key] = True
         self.event('mutable', x)
         source = InstanceSource(self, x)
-        for attr in source.all_instance_attributes():
-            clsdef.add_source_for_attribute(attr, source) # can trigger reflowing
+        def delayed():
+            for attr in source.all_instance_attributes():
+                clsdef.add_source_for_attribute(attr, source)
+                # ^^^ can trigger reflowing
+        self._see_mutable_flattenrec(delayed)
 
     def valueoftype(self, t):
         return annotationoftype(t, self)
diff --git a/rpython/rtyper/rclass.py b/rpython/rtyper/rclass.py
--- a/rpython/rtyper/rclass.py
+++ b/rpython/rtyper/rclass.py
@@ -7,6 +7,7 @@
 from rpython.rlib.objectmodel import UnboxedValue
 from rpython.tool.pairtype import pairtype, pair
 from rpython.tool.identity_dict import identity_dict
+from rpython.tool.flattenrec import FlattenRecursion
 from rpython.rtyper.extregistry import ExtRegistryEntry
 from rpython.rtyper.error import TyperError
 from rpython.rtyper.lltypesystem import lltype
@@ -767,11 +768,14 @@
             self.initialize_prebuilt_data(Ellipsis, self.classdef, result)
             return result
 
+    _initialize_data_flattenrec = FlattenRecursion()
+
     def initialize_prebuilt_instance(self, value, classdef, result):
         # must fill in the hash cache before the other ones
         # (see test_circular_hash_initialization)
         self.initialize_prebuilt_hash(value, result)
-        self.initialize_prebuilt_data(value, classdef, result)
+        self._initialize_data_flattenrec(self.initialize_prebuilt_data,
+                                         value, classdef, result)
 
     def get_ll_hash_function(self):
         return ll_inst_hash
diff --git a/rpython/rtyper/test/test_rclass.py b/rpython/rtyper/test/test_rclass.py
--- a/rpython/rtyper/test/test_rclass.py
+++ b/rpython/rtyper/test/test_rclass.py
@@ -1279,3 +1279,16 @@
             return cls[k](a, b).b
 
         assert self.interpret(f, [1, 4, 7]) == 7
+
+    def test_flatten_convert_const(self):
+        # check that we can convert_const() a chain of more than 1000
+        # instances
+        class A(object):
+            def __init__(self, next):
+                self.next = next
+        a = None
+        for i in range(1500):
+            a = A(a)
+        def f():
+            return a.next.next.next.next is not None
+        assert self.interpret(f, []) == True
diff --git a/rpython/tool/flattenrec.py b/rpython/tool/flattenrec.py
new file mode 100644
--- /dev/null
+++ b/rpython/tool/flattenrec.py
@@ -0,0 +1,25 @@
+"""
+A general way to flatten deeply recursive algorithms by delaying some
+parts until later.
+"""
+
+
+class FlattenRecursion(object):
+
+    def __init__(self):
+        self.later = None
+
+    def __call__(self, func, *args, **kwds):
+        """Call func(*args, **kwds), either now, or, if we're recursing,
+        then the call will be done later by the first level.
+        """
+        if self.later is not None:
+            self.later.append((func, args, kwds))
+        else:
+            self.later = lst = []
+            try:
+                func(*args, **kwds)
+                for func, args, kwds in lst:
+                    func(*args, **kwds)
+            finally:
+                self.later = None
diff --git a/rpython/tool/test/test_flattenrec.py b/rpython/tool/test/test_flattenrec.py
new file mode 100644
--- /dev/null
+++ b/rpython/tool/test/test_flattenrec.py
@@ -0,0 +1,13 @@
+from rpython.tool.flattenrec import FlattenRecursion
+
+def test_flattenrec():
+    r = FlattenRecursion()
+    seen = set()
+
+    def rec(n):
+        if n > 0:
+            r(rec, n-1)
+        seen.add(n)
+
+    rec(10000)
+    assert seen == set(range(10001))


More information about the pypy-commit mailing list