[pypy-commit] pypy default: Tweak the logic for ll_isinstance(): in the common case of no
arigo
noreply at buildbot.pypy.org
Sun Nov 23 23:54:23 CET 2014
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r74654:c286dc658327
Date: 2014-11-23 23:54 +0100
http://bitbucket.org/pypy/pypy/changeset/c286dc658327/
Log: Tweak the logic for ll_isinstance(): in the common case of no
subclasses, improve the performance (a little bit). Fixes an age-
old "XXX re-implement the following optimization".
diff --git a/rpython/rtyper/normalizecalls.py b/rpython/rtyper/normalizecalls.py
--- a/rpython/rtyper/normalizecalls.py
+++ b/rpython/rtyper/normalizecalls.py
@@ -298,12 +298,16 @@
# ____________________________________________________________
+class TooLateForNewSubclass(Exception):
+ pass
+
class TotalOrderSymbolic(ComputedIntSymbolic):
def __init__(self, orderwitness, peers):
self.orderwitness = orderwitness
self.peers = peers
self.value = None
+ self._with_subclasses = None # unknown
peers.append(self)
def __cmp__(self, other):
@@ -320,12 +324,34 @@
def __rsub__(self, other):
return other - self.compute_fn()
+ def check_any_subclass_in_peer_list(self, i):
+ # check if the next peer, in order, is or not the end
+ # marker for this start marker
+ assert self.peers[i] is self
+ return self.peers[i + 1].orderwitness != self.orderwitness + [MAX]
+
+ def number_with_subclasses(self):
+ # Return True or False depending on whether this is the
+ # subclassrange_min corresponding to a class which has subclasses
+ # or not. If this is called and returns False, then adding later
+ # new subclasses will crash in compute_fn().
+ if self._with_subclasses is None: # unknown so far
+ self.peers.sort()
+ i = self.peers.index(self)
+ self._with_subclasses = self.check_any_subclass_in_peer_list(i)
+ return self._with_subclasses
+
def compute_fn(self):
if self.value is None:
self.peers.sort()
for i, peer in enumerate(self.peers):
assert peer.value is None or peer.value == i
peer.value = i
+ #
+ if peer._with_subclasses is False:
+ if peer.check_any_subclass_in_peer_list(i):
+ raise TooLateForNewSubclass
+ #
assert self.value is not None
return self.value
diff --git a/rpython/rtyper/rclass.py b/rpython/rtyper/rclass.py
--- a/rpython/rtyper/rclass.py
+++ b/rpython/rtyper/rclass.py
@@ -1007,14 +1007,11 @@
v_obj, v_cls = hop.inputargs(instance_repr, class_repr)
if isinstance(v_cls, Constant):
cls = v_cls.value
- # XXX re-implement the following optimization
- #if cls.subclassrange_max == cls.subclassrange_min:
- # # a class with no subclass
- # return hop.gendirectcall(rclass.ll_isinstance_exact, v_obj, v_cls)
- #else:
- minid = hop.inputconst(Signed, cls.subclassrange_min)
- maxid = hop.inputconst(Signed, cls.subclassrange_max)
- return hop.gendirectcall(ll_isinstance_const, v_obj, minid, maxid)
+ llf, llf_nonnull = make_ll_isinstance(self.rtyper, cls)
+ if hop.args_s[0].can_be_None:
+ return hop.gendirectcall(llf, v_obj)
+ else:
+ return hop.gendirectcall(llf_nonnull, v_obj)
else:
return hop.gendirectcall(ll_isinstance, v_obj, v_cls)
@@ -1128,16 +1125,26 @@
obj_cls = obj.typeptr
return ll_issubclass(obj_cls, cls)
-def ll_isinstance_const(obj, minid, maxid):
- if not obj:
- return False
- return ll_issubclass_const(obj.typeptr, minid, maxid)
-
-def ll_isinstance_exact(obj, cls):
- if not obj:
- return False
- obj_cls = obj.typeptr
- return obj_cls == cls
+def make_ll_isinstance(rtyper, cls):
+ try:
+ return rtyper.isinstance_helpers[cls._obj]
+ except KeyError:
+ minid = cls.subclassrange_min
+ maxid = cls.subclassrange_max
+ if minid.number_with_subclasses():
+ def ll_isinstance_const_nonnull(obj):
+ objid = obj.typeptr.subclassrange_min
+ return llop.int_between(Bool, minid, objid, maxid)
+ else:
+ def ll_isinstance_const_nonnull(obj):
+ return obj.typeptr == cls
+ def ll_isinstance_const(obj):
+ if not obj:
+ return False
+ return ll_isinstance_const_nonnull(obj)
+ result = (ll_isinstance_const, ll_isinstance_const_nonnull)
+ rtyper.isinstance_helpers[cls._obj] = result
+ return result
def ll_runtime_type_info(obj):
return obj.typeptr.rtti
diff --git a/rpython/rtyper/rtyper.py b/rpython/rtyper/rtyper.py
--- a/rpython/rtyper/rtyper.py
+++ b/rpython/rtyper/rtyper.py
@@ -59,6 +59,7 @@
self.typererror_count = 0
# make the primitive_to_repr constant mapping
self.primitive_to_repr = {}
+ self.isinstance_helpers = {}
self.exceptiondata = ExceptionData(self)
self.custom_trace_funcs = []
diff --git a/rpython/rtyper/test/test_normalizecalls.py b/rpython/rtyper/test/test_normalizecalls.py
--- a/rpython/rtyper/test/test_normalizecalls.py
+++ b/rpython/rtyper/test/test_normalizecalls.py
@@ -6,6 +6,7 @@
from rpython.rtyper.test.test_llinterp import interpret
from rpython.rtyper.lltypesystem import lltype
from rpython.rtyper.normalizecalls import TotalOrderSymbolic, MAX
+from rpython.rtyper.normalizecalls import TooLateForNewSubclass
def test_TotalOrderSymbolic():
@@ -21,6 +22,49 @@
assert t1 <= 5
assert t1.value == 0
+def test_TotalOrderSymbolic_with_subclasses():
+ lst = []
+ t3 = TotalOrderSymbolic([3, 4, 2, MAX], lst)
+ t1 = TotalOrderSymbolic([3, 4], lst)
+ t2 = TotalOrderSymbolic([3, 4, 2], lst)
+ t4 = TotalOrderSymbolic([3, 4, MAX], lst)
+ assert t1.number_with_subclasses()
+ assert not t2.number_with_subclasses()
+ assert [t.compute_fn() for t in [t1, t2, t3, t4]] == range(4)
+ #
+ lst = []
+ t1 = TotalOrderSymbolic([3, 4], lst)
+ t3 = TotalOrderSymbolic([3, 4, 2, MAX], lst)
+ t4 = TotalOrderSymbolic([3, 4, MAX], lst)
+ t2 = TotalOrderSymbolic([3, 4, 2], lst)
+ assert not t2.number_with_subclasses()
+ assert t1.number_with_subclasses()
+ assert [t.compute_fn() for t in [t1, t2, t3, t4]] == range(4)
+ #
+ lst = []
+ t1 = TotalOrderSymbolic([3, 4], lst)
+ t4 = TotalOrderSymbolic([3, 4, MAX], lst)
+ assert not t1.number_with_subclasses()
+ t2 = TotalOrderSymbolic([3, 4, 2], lst)
+ t3 = TotalOrderSymbolic([3, 4, 2, MAX], lst)
+ py.test.raises(TooLateForNewSubclass, t2.compute_fn)
+ #
+ lst = []
+ t1 = TotalOrderSymbolic([3, 4], lst)
+ t4 = TotalOrderSymbolic([3, 4, MAX], lst)
+ assert not t1.number_with_subclasses()
+ t2 = TotalOrderSymbolic([1], lst)
+ t3 = TotalOrderSymbolic([1, MAX], lst)
+ assert [t.compute_fn() for t in [t2, t3, t1, t4]] == range(4)
+ #
+ lst = []
+ t1 = TotalOrderSymbolic([3, 4], lst)
+ t4 = TotalOrderSymbolic([3, 4, MAX], lst)
+ assert not t1.number_with_subclasses()
+ t2 = TotalOrderSymbolic([6], lst)
+ t3 = TotalOrderSymbolic([6, MAX], lst)
+ assert [t.compute_fn() for t in [t1, t4, t2, t3]] == range(4)
+
# ____________________________________________________________
class TestNormalize(object):
More information about the pypy-commit
mailing list