Commits

Carl Friedrich Bolz committed f8cf489

optimize the ShapedCallable classes for a fixed number of arguments. needs to be tuned, etc.

  • Participants
  • Parent commits d1d415b
  • Branches compress-terms2

Comments (0)

Files changed (3)

File prolog/interpreter/shape.py

-from pypy.rlib import jit, objectmodel, debug
+from pypy.rlib import jit, objectmodel, debug, unroll
 from prolog.interpreter import term
 # a Callable implementation that tries to save memory
 
 # XXX tune this
 MAX_DEPTH = 10
 MAX_SIZE = 10
+SHAPED_CALLABLE_SIZE = 10
 
 class Shape(object):
     _attrs_ = []
             mode = intmask((1000003 * mode) ^ y)
         return mode
 
+unroll_n = unroll.unrolling_iterable(range(SHAPED_CALLABLE_SIZE))
+unroll_r = unroll.unrolling_iterable(range(SHAPED_CALLABLE_SIZE)[::-1])
+
 class ShapedCallableMixin:
     TYPE_STANDARD_ORDER = term.Term.TYPE_STANDARD_ORDER
     _mixin_ = True
     def __init__(self, shape, storage):
         assert isinstance(shape, SharingShape)
         self.shape = shape
-        storage = debug.make_sure_not_resized(storage)
-        self.storage = storage
         assert shape.num_storage_vars() == len(storage)
+        self.set_full_storage(storage)
 
     def get_shape(self):
         return jit.promote(self.shape)
         self.shape = shape
 
     def get_storage(self, i):
-        return self.storage[i]
+        for n in unroll_n:
+            if i == n:
+                return getattr(self, "a%s" % n)
+        return self.rest_storage[i - SHAPED_CALLABLE_SIZE]
 
     def set_storage(self, i, val):
-        self.storage[i] = val
+        for n in unroll_n:
+            if i == n:
+                setattr(self, "a%s" % n, val)
+                break
+        else:
+            self.rest_storage[i - SHAPED_CALLABLE_SIZE] = val
 
     def size_storage(self):
         return self.get_shape().num_storage_vars()
 
     def get_full_storage(self):
-        return self.storage
+        result = [None] * self.size_storage()
+        for i in range(len(result)):
+            result[i] = self.get_storage(i)
+        return result
 
     def set_full_storage(self, storage):
-        self.storage = storage
-
+        # this is very much over the top, but it was fun to do
+        size = self.size_storage()
+        self.rest_storage = None
+        if size == 0:
+            return
+        # the trick: "promote" size
+        for n in unroll_n:
+            if size == n + 1:
+                break
+        else:
+            self.rest_storage = storage[SHAPED_CALLABLE_SIZE:]
+            n = SHAPED_CALLABLE_SIZE - 1
+        for i in unroll_r:
+            if n == i:
+                setattr(self, "a%s" % i, storage[i])
+                n = i - 1
 
     # _____________________________________________________________________
     # callable interface
             cloned = arg.copy_standardize_apart_as_child_of(heap, env, result, i)
             newinstance = newinstance | (isinstance(arg, term.NumberedVar) or cloned is not arg)
             needmutable = needmutable | isinstance(cloned, term.VarInTerm)
-            storage[i] = cloned
+            result.set_storage(i, cloned)
         if newinstance:
             if not needmutable:
                 return result._make_immutable()
                 if (isinstance(indicator, term.VarInTermIndex) and
                         indicator.index == i):
                     child.indicator = term.VarInTermIndex.build(i + offset)
+        self.set_shape(new_shape)
         self.set_full_storage(newstorage)
-        self.set_shape(new_shape)
         return self
 
     def replace_child(self, index, obj):
 
 
 class ShapedCallable(ShapedCallableMixin, ShapedCallableBase):
-    _immutable_fields_ = ["shape", "storage[*]"]
+    _immutable_fields_ = ["shape", "rest_storage[*]"] + ["a%s" % i for i in range(SHAPED_CALLABLE_SIZE)]
 
     def _make_mutable(self):
         return ShapedCallableMutable(self.get_shape(), self.get_full_storage())

File prolog/interpreter/term.py

         path = obj.get_shape().get_path(index)
         newobj = obj.replace_child(index, value)
         if newobj is None:
-            obj.storage[index] = value
+            obj.set_storage(index, value)
         else:
             assert newobj is obj
         self.indicator = path

File prolog/interpreter/test/test_shape.py

     assert isinstance(s.children[2], shape.InStorageShape)
     assert isinstance(s.children[3], shape.InStorageShape)
     w_obj = std.make_shaped_callable([4, 5], None)
-    assert w_obj.storage == [4, 5]
+    assert w_obj.get_full_storage() == [4, 5]
 
 
     w_obj = term.Callable.build("f", [term.Callable.build("a"),
         def newvar(self):
             return 7
     w_obj = std.make_shaped_callable([4, 5], FakeHeap())
-    assert w_obj.storage == [4, 4, 5, 7]
+    assert w_obj.get_full_storage() == [4, 4, 5, 7]
     w_obj = std.make_shaped_callable([4, None], FakeHeap())
-    assert w_obj.storage == [4, 4, 7, 7]
+    assert w_obj.get_full_storage() == [4, 4, 7, 7]
 
     class FakeHeap(object):
         def newvar(self):
             return object()
     w_obj = std.make_shaped_callable([None, None], FakeHeap())
-    assert w_obj.storage[0] is w_obj.storage[1]
+    assert w_obj.get_storage(0) is w_obj.get_storage(1)
 
 def test_replace():
     sig = signature.Signature.getsignature(".", 2)
     c2 = shape.ShapedCallable(s1, [b, nil])
     newshape = s1.replace(1, s1)
     c1._replace_child(1, c2, newshape)
-    assert c1.storage == [a, b, nil]
+    assert c1.get_full_storage() == [a, b, nil]
 
     c1 = shape.ShapedCallable(s1, [None, a])
     c2 = shape.ShapedCallable(s1, [b, nil])
     newshape = s1.replace(0, s1)
     c1._replace_child(0, c2, newshape)
-    assert c1.storage == [b, nil, a]
+    assert c1.get_full_storage() == [b, nil, a]
 
 def test_replace_child_fixup_varinterm_at_end():
     from prolog.interpreter.heap import Heap
 
     c2 = shape.ShapedCallableMutable(s1, [b, c, c])
     var1 = h.newvar_in_term(c1, 2)
-    c1.storage[2] = var1
+    c1.set_storage(2, var1)
 
     s1.get_transition(1, s1)
     s2 = s1.get_transition(1, s1)
     c1._replace_child(1, c2, s2)
-    assert c1.storage[4].parent is c1
-    assert c1.storage[4].indicator.index == 4
+    assert c1.get_storage(4).parent is c1
+    assert c1.get_storage(4).indicator.index == 4
 
 
 def test_replace_child_fixup_varinterm_from_replacement():
 
     c2 = shape.ShapedCallableMutable(s1, [b, None, c])
     var2 = h.newvar_in_term(c2, 1)
-    c2.storage[1] = var2
+    c2.set_storage(1, var2)
 
     s1.get_transition(1, s1)
     res = c1.replace_child(1, c2)
     assert res is c1
-    assert c1.storage[2].parent is c1
-    assert c1.storage[2].indicator.index == 2
+    assert c1.get_storage(2).parent is c1
+    assert c1.get_storage(2).indicator.index == 2
 
     c1 = shape.ShapedCallable(s1, [a, None, c])
     c2 = shape.ShapedCallableMutable(s1, [b, None, c])
     var2 = h.newvar_in_term(c2, 1)
-    c2.storage[1] = var2
+    c2.set_storage(1, var2)
 
     s1.get_transition(1, s1)
     res = c1.replace_child(1, c2)
     assert isinstance(res, shape.ShapedCallableMutable)
-    assert res.storage[2].parent is res
-    assert res.storage[2].indicator.index == 2
+    assert res.get_storage(2).parent is res
+    assert res.get_storage(2).indicator.index == 2
 
 def test_depth():
     sig = signature.Signature.getsignature(".", 2)
     c2 = shape.ShapedCallable(s1, [3, c1])
     c3 = shape.ShapedCallable.build(s1, [4, c2])
     assert c3.shape is s3
-    assert c3.storage == [4, 3, 2, nil]
+    assert c3.get_full_storage() == [4, 3, 2, nil]
     c4 = shape.ShapedCallable.build(s1, [4, c2])
-    assert c4.storage == [4, 3, 2]
+    assert c4.get_full_storage() == [4, 3, 2]
 
 def test_shaped_callable_unify():
     from prolog.interpreter import heap
         res = res.argument_at(1)
     assert l == [1, 2, 3, 4, 5, 2, 3, 4, 5, 6]
     res = env['X']
-    assert len(res.storage) > 5
+    assert len(res.get_full_storage()) > 5
 
     for i in range(10):
         env = assert_true("reverse([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [], X).", e)
         res = res.argument_at(1)
     assert l == [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
     res = env['X']
-    assert len(res.storage) > 5
+    assert len(res.get_full_storage()) > 5