Author: Armin Rigo <ar...@tunes.org> Branch: SpecialisedTuples Changeset: r50297:363bac62f6dc Date: 2011-12-08 14:23 +0100 http://bitbucket.org/pypy/pypy/changeset/363bac62f6dc/
Log: Rework the source code. Kill a few features that don't really make sense to have; write by hand a fast decision tree to pick which tuple to specialize for; general fixes left and right. diff --git a/pypy/objspace/std/specialisedtupleobject.py b/pypy/objspace/std/specialisedtupleobject.py --- a/pypy/objspace/std/specialisedtupleobject.py +++ b/pypy/objspace/std/specialisedtupleobject.py @@ -4,35 +4,27 @@ from pypy.objspace.std.multimethod import FailedToImplement from pypy.objspace.std.tupleobject import W_TupleObject from pypy.objspace.std.sliceobject import W_SliceObject, normalize_simple_slice -from pypy.objspace.std.floatobject import _hash_float from pypy.rlib.rarithmetic import intmask from pypy.rlib.objectmodel import compute_hash from pypy.rlib.unroll import unrolling_iterable -class Any(object): +class NotSpecialised(Exception): pass -class NotSpecialised(Exception): - pass - -_specialisations = [] - -def makespecialisedtuple(space, list_w): - for specialisedClass in unrolling_iterable(_specialisations): - try: - return specialisedClass.try_specialisation(space, list_w) - except NotSpecialised: - pass - raise NotSpecialised - class W_SpecialisedTupleObject(W_Object): from pypy.objspace.std.tupletype import tuple_typedef as typedef __slots__ = [] + def __repr__(self): + """ representation for debugging purposes """ + reprlist = [repr(item) for item in self._to_unwrapped_list()] + return "%s(%s)" % (self.__class__.__name__, ', '.join(reprlist)) + def tolist(self): raise NotImplementedError - def _tolistunwrapped(self): + def _to_unwrapped_list(self): + "NOT_RPYTHON" raise NotImplementedError def length(self): @@ -52,7 +44,7 @@ def unwrap(self, space): return tuple(self._to_unwrapped_list()) - + def make_specialised_class(typetuple): assert type(typetuple) == tuple @@ -61,79 +53,68 @@ iter_n = unrolling_iterable(range(nValues)) class cls(W_SpecialisedTupleObject): - def __init__(self, space, values): - #print cls,cls.__class__, values + def __init__(self, space, *values): + self.space = space assert len(values) == nValues for i in iter_n: - if typetuple[i] != Any: + if typetuple[i] != object: assert isinstance(values[i], typetuple[i]) - self.space = space + setattr(self, 'value%s' % i, values[i]) + + @classmethod + def make(cls, space, *values_w): + unwrappedparams = () for i in iter_n: - setattr(self, 'value%s' % i, values[i]) - - - @classmethod - def try_specialisation(cls, space, paramlist): - if len(paramlist) != nValues: - raise NotSpecialised - for param,val_type in unrolling_iterable(zip(paramlist, typetuple)): + w_obj = values_w[i] + val_type = typetuple[i] if val_type == int: - if space.type(param) != space.w_int: - raise NotSpecialised + unwrapped = space.int_w(w_obj) elif val_type == float: - if space.type(param) != space.w_float: - raise NotSpecialised + unwrapped = space.float_w(w_obj) elif val_type == str: - if space.type(param) != space.w_str: - raise NotSpecialised - elif val_type == Any: - pass + unwrapped = space.str_w(w_obj) + elif val_type == object: + unwrapped = w_obj else: - raise NotSpecialised - unwrappedparams = [None] * nValues - for i in iter_n: - if typetuple[i] == int: - unwrappedparams[i] = space.int_w(paramlist[i]) - elif typetuple[i] == float: - unwrappedparams[i] = space.float_w(paramlist[i]) - elif typetuple[i] == str: - unwrappedparams[i] = space.str_w(paramlist[i]) - elif typetuple[i] == Any: - unwrappedparams[i] = paramlist[i] - else: - raise NotSpecialised - return cls(space, unwrappedparams) - + raise AssertionError + unwrappedparams += (unwrapped,) + return cls(space, *unwrappedparams) + def length(self): return nValues - + def tolist(self): list_w = [None] * nValues for i in iter_n: - if typetuple[i] == Any: - list_w[i] = getattr(self, 'value%s' % i) - else: - list_w[i] = self.space.wrap(getattr(self, 'value%s' % i)) + value = getattr(self, 'value%s' % i) + if typetuple[i] != object: + value = self.space.wrap(value) + list_w[i] = value return list_w - + def _to_unwrapped_list(self): - list_w = [None] * nValues + "NOT_RPYTHON" + list_w = [None] * nValues for i in iter_n: - if typetuple[i] == Any: - list_w[i] = space.unwrap(getattr(self, 'value%s' % i))#xxx - else: - list_w[i] = getattr(self, 'value%s' % i) + value = getattr(self, 'value%s' % i) + if typetuple[i] == object: + value = self.space.unwrap(value) + list_w[i] = value return list_w - + def hash(self, space): + # XXX duplicate logic from tupleobject.py mult = 1000003 x = 0x345678 z = 2 for i in iter_n: value = getattr(self, 'value%s' % i) - if typetuple[i] == Any: - y = space.int_w(space.hash(value)) - elif typetuple[i] == float: # get correct hash for float which is an integer & other less frequent cases + if typetuple[i] == object: + y = space.int_w(space.hash(value)) + elif typetuple[i] == float: + # get the correct hash for float which is an + # integer & other less frequent cases + from pypy.objspace.std.floatobject import _hash_float y = _hash_float(space, value) else: y = compute_hash(value) @@ -142,57 +123,109 @@ mult += 82520 + z + z x += 97531 return space.wrap(intmask(x)) - + def _eq(self, w_other): - if not isinstance(w_other, cls): #so we will be sure we are comparing same types + if not isinstance(w_other, cls): + # if we are not comparing same types, give up raise FailedToImplement for i in iter_n: - if typetuple[i] == Any: - if not self.space.is_true(self.space.eq(getattr(self, 'value%s' % i), getattr(w_other, 'value%s' % i))): - return False + myval = getattr(self, 'value%s' % i) + otherval = getattr(w_other, 'value%s' % i) + if typetuple[i] == object: + if not self.space.eq_w(myval, otherval): + return False else: - if getattr(self, 'value%s' % i) != getattr(w_other, 'value%s' % i): - return False + if myval != otherval: + return False else: return True - + def eq(self, space, w_other): return space.newbool(self._eq(w_other)) - + def ne(self, space, w_other): return space.newbool(not self._eq(w_other)) - - def _compare(self, compare_op, w_other): - if not isinstance(w_other, cls): - raise FailedToImplement - ncmp = min(self.length(), w_other.length()) - for i in iter_n: - if typetuple[i] == Any:#like space.eq on wrapped or two params? - raise FailedToImplement - if ncmp > i: - l_val = getattr(self, 'value%s' % i) - r_val = getattr(w_other, 'value%s' % i) - if l_val != r_val: - return compare_op(l_val, r_val) - return compare_op(self.length(), w_other.length()) - + +## def _compare(self, compare_op, w_other): +## if not isinstance(w_other, cls): +## raise FailedToImplement +## ncmp = min(self.length(), w_other.length()) +## for i in iter_n: +## if typetuple[i] == Any:#like space.eq on wrapped or two params? +## raise FailedToImplement +## if ncmp > i: +## l_val = getattr(self, 'value%s' % i) +## r_val = getattr(w_other, 'value%s' % i) +## if l_val != r_val: +## return compare_op(l_val, r_val) +## return compare_op(self.length(), w_other.length()) + def getitem(self, index): for i in iter_n: if index == i: - if typetuple[i] == Any: - return getattr(self, 'value%s' % i) - else: - return self.space.wrap(getattr(self, 'value%s' % i)) + value = getattr(self, 'value%s' % i) + if typetuple[i] != object: + value = self.space.wrap(value) + return value raise IndexError - cls.__name__ = 'W_SpecialisedTupleObject' + ''.join([t.__name__.capitalize() for t in typetuple]) + cls.__name__ = ('W_SpecialisedTupleObject_' + + ''.join([t.__name__[0] for t in typetuple])) _specialisations.append(cls) return cls -make_specialised_class((float, float)) -for _typ1 in [int, str, Any]: - for _typ2 in [int, str, Any]: - make_specialised_class((_typ1, _typ2)) +# ---------- current specialized versions ---------- + +_specialisations = [] +Cls_ii = make_specialised_class((int, int)) +Cls_is = make_specialised_class((int, str)) +Cls_io = make_specialised_class((int, object)) +Cls_si = make_specialised_class((str, int)) +Cls_ss = make_specialised_class((str, str)) +Cls_so = make_specialised_class((str, object)) +Cls_oi = make_specialised_class((object, int)) +Cls_os = make_specialised_class((object, str)) +Cls_oo = make_specialised_class((object, object)) +Cls_ff = make_specialised_class((float, float)) +Cls_ooo = make_specialised_class((object, object, object)) + +def makespecialisedtuple(space, list_w): + if len(list_w) == 2: + w_arg1, w_arg2 = list_w + w_type1 = space.type(w_arg1) + w_type2 = space.type(w_arg2) + # + if w_type1 is space.w_int: + if w_type2 is space.w_int: + return Cls_ii.make(space, w_arg1, w_arg2) + elif w_type2 is space.w_str: + return Cls_is.make(space, w_arg1, w_arg2) + else: + return Cls_io.make(space, w_arg1, w_arg2) + # + elif w_type1 is space.w_str: + if w_type2 is space.w_int: + return Cls_si.make(space, w_arg1, w_arg2) + elif w_type2 is space.w_str: + return Cls_ss.make(space, w_arg1, w_arg2) + else: + return Cls_so.make(space, w_arg1, w_arg2) + # + elif w_type1 is space.w_float and w_type2 is space.w_float: + return Cls_ff.make(space, w_arg1, w_arg2) + # + else: + if w_type2 is space.w_int: + return Cls_oi.make(space, w_arg1, w_arg2) + elif w_type2 is space.w_str: + return Cls_os.make(space, w_arg1, w_arg2) + else: + return Cls_oo.make(space, w_arg1, w_arg2) + # + elif len(list_w) == 3: + return Cls_ooo.make(space, list_w[0], list_w[1], list_w[2]) + else: + raise NotSpecialised # ____________________________________________________________ @@ -224,23 +257,23 @@ start += step return space.newtuple(subitems) -def mul_specialisedtuple_times(space, w_tuple, w_times): - try: - times = space.getindex_w(w_times, space.w_OverflowError) - except OperationError, e: - if e.match(space, space.w_TypeError): - raise FailedToImplement - raise - if times == 1 and space.type(w_tuple) == space.w_tuple: - return w_tuple - items = w_tuple.tolist() - return space.newtuple(items * times) +##def mul_specialisedtuple_times(space, w_tuple, w_times): +## try: +## times = space.getindex_w(w_times, space.w_OverflowError) +## except OperationError, e: +## if e.match(space, space.w_TypeError): +## raise FailedToImplement +## raise +## if times == 1 and space.type(w_tuple) == space.w_tuple: +## return w_tuple +## items = w_tuple.tolist() +## return space.newtuple(items * times) -def mul__SpecialisedTuple_ANY(space, w_tuple, w_times): - return mul_specialisedtuple_times(space, w_tuple, w_times) +##def mul__SpecialisedTuple_ANY(space, w_tuple, w_times): +## return mul_specialisedtuple_times(space, w_tuple, w_times) -def mul__ANY_SpecialisedTuple(space, w_times, w_tuple): - return mul_specialisedtuple_times(space, w_tuple, w_times) +##def mul__ANY_SpecialisedTuple(space, w_times, w_tuple): +## return mul_specialisedtuple_times(space, w_tuple, w_times) def eq__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2): return w_tuple1.eq(space, w_tuple2) @@ -248,19 +281,19 @@ def ne__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2): return w_tuple1.ne(space, w_tuple2) -from operator import lt, le, ge, gt +##from operator import lt, le, ge, gt -def lt__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2): - return space.newbool(w_tuple1._compare(lt, w_tuple2)) +##def lt__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2): +## return space.newbool(w_tuple1._compare(lt, w_tuple2)) -def le__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2): - return space.newbool(w_tuple1._compare(le, w_tuple2)) +##def le__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2): +## return space.newbool(w_tuple1._compare(le, w_tuple2)) -def ge__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2): - return space.newbool(w_tuple1._compare(ge, w_tuple2)) +##def ge__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2): +## return space.newbool(w_tuple1._compare(ge, w_tuple2)) -def gt__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2): - return space.newbool(w_tuple1._compare(gt, w_tuple2)) +##def gt__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2): +## return space.newbool(w_tuple1._compare(gt, w_tuple2)) def hash__SpecialisedTuple(space, w_tuple): return w_tuple.hash(space) diff --git a/pypy/objspace/std/test/test_specialisedtupleobject.py b/pypy/objspace/std/test/test_specialisedtupleobject.py --- a/pypy/objspace/std/test/test_specialisedtupleobject.py +++ b/pypy/objspace/std/test/test_specialisedtupleobject.py @@ -1,10 +1,10 @@ -import py +import py, sys from pypy.objspace.std.tupleobject import W_TupleObject from pypy.objspace.std.specialisedtupleobject import W_SpecialisedTupleObject from pypy.objspace.std.specialisedtupleobject import _specialisations from pypy.interpreter.error import OperationError from pypy.conftest import gettestobjspace -from pypy.objspace.std.test.test_tupleobject import AppTestW_TupleObject +from pypy.objspace.std.test import test_tupleobject from pypy.interpreter import gateway @@ -19,7 +19,7 @@ def test_isspecialisedtupleobjectintint(self): w_tuple = self.space.newtuple([self.space.wrap(1), self.space.wrap(2)]) - assert isinstance(w_tuple, W_SpecialisedTupleObjectIntInt) + assert isinstance(w_tuple, W_SpecialisedTupleObject_ii) def test_isnotspecialisedtupleobject(self): w_tuple = self.space.newtuple([self.space.wrap({})]) @@ -27,8 +27,8 @@ def test_specialisedtupleclassname(self): w_tuple = self.space.newtuple([self.space.wrap(1), self.space.wrap(2)]) - assert w_tuple.__class__.__name__ == 'W_SpecialisedTupleObjectIntInt' - + assert w_tuple.__class__.__name__ == 'W_SpecialisedTupleObject_ii' + def test_hash_against_normal_tuple(self): N_space = gettestobjspace(**{"objspace.std.withspecialisedtuple": False}) S_space = gettestobjspace(**{"objspace.std.withspecialisedtuple": True}) @@ -62,13 +62,17 @@ assert len(list_w) == 1 assert self.space.eq_w(list_w[0], self.space.wrap(5)) -class AppTestW_SpecialisedTupleObject(AppTestW_TupleObject): +class AppTestW_SpecialisedTupleObject: def setup_class(cls): cls.space = gettestobjspace(**{"objspace.std.withspecialisedtuple": True}) def forbid_delegation(space, w_tuple): def delegation_forbidden(): - raise NotImplementedError + # haaaack + if sys._getframe(2).f_code.co_name == '_mm_repr_tupleS0': + return old_tolist() + raise NotImplementedError, w_tuple + old_tolist = w_tuple.tolist w_tuple.tolist = delegation_forbidden return w_tuple cls.w_forbid_delegation = cls.space.wrap(gateway.interp2app(forbid_delegation)) @@ -80,20 +84,23 @@ return ("SpecialisedTupleObject" + expected) in r def test_createspecialisedtuple(self): - spec = {int: 'Int', - float: 'Float', - str: 'Str', - list: 'Any'} + spec = {int: 'i', + float: 'f', + str: 's', + list: 'o'} # for x in [42, 4.2, "foo", []]: for y in [43, 4.3, "bar", []]: expected1 = spec[type(x)] expected2 = spec[type(y)] - if (expected1 == 'Float') ^ (expected2 == 'Float'): - if expected1 == 'Float': expected1 = 'Any' - if expected2 == 'Float': expected2 = 'Any' + if (expected1 == 'f') ^ (expected2 == 'f'): + if expected1 == 'f': expected1 = 'o' + if expected2 == 'f': expected2 = 'o' obj = (x, y) - assert self.isspecialised(obj, expected1 + expected2) + assert self.isspecialised(obj, '_' + expected1 + expected2) + # + obj = (1, 2, 3) + assert self.isspecialised(obj, '_ooo') def test_len(self): t = self.forbid_delegation((42,43)) @@ -104,31 +111,37 @@ assert not self.isspecialised((1.5,)) def test_slicing_to_specialised(self): - assert self.isspecialised((1, 2, 3)[0:2]) - assert self.isspecialised((1, '2', 3)[0:5:2]) + t = (1, 2, 3) + assert self.isspecialised(t[0:2]) + t = (1, '2', 3) + assert self.isspecialised(t[0:5:2]) def test_adding_to_specialised(self): - assert self.isspecialised((1,)+(2,)) + t = (1,) + assert self.isspecialised(t + (2,)) def test_multiply_to_specialised(self): - assert self.isspecialised((1,)*2) + t = (1,) + assert self.isspecialised(t * 2) def test_slicing_from_specialised(self): - assert (1,2,3)[0:2:1] == (1,2) + t = (1, 2, 3) + assert t[0:2:1] == (1, 2) def test_eq_no_delegation(self): - a = self.forbid_delegation((1,2)) - b = (1,2) + t = (1,) + a = self.forbid_delegation(t + (2,)) + b = (1, 2) assert a == b - - c = (2,1) + + c = (2, 1) assert not a == c - + def test_eq_can_delegate(self): a = (1,2) b = (1,3,2) assert not a == b - + values = [2, 2L, 2.0, 1, 1L, 1.0] for x in values: for y in values: @@ -144,11 +157,11 @@ assert a != c def test_ordering(self): - a = self.forbid_delegation((1,2)) + a = (1,2) #self.forbid_delegation((1,2)) --- code commented out assert a < (2,2) assert a < (1,3) assert not a < (1,2) - + assert a <= (2,2) assert a <= (1,2) assert not a <= (1,1) @@ -160,15 +173,34 @@ assert a > (0,2) assert a > (1,1) assert not a > (1,3) + + assert (2,2) > a + assert (1,3) > a + assert not (1,2) > a + + assert (2,2) >= a + assert (1,2) >= a + assert not (1,1) >= a + + assert (0,2) <= a + assert (1,2) <= a + assert not (1,3) <= a + assert (0,2) < a + assert (1,1) < a + assert not (1,3) < a + def test_hash(self): a = (1,2) - b = (1,) + (2,) # else a and b refer to same constant + b = (1,) + b += (2,) # else a and b refer to same constant assert hash(a) == hash(b) c = (2,4) assert hash(a) != hash(c) + assert hash(a) == hash((1L, 2L)) == hash((1.0, 2.0)) == hash((1.0, 2L)) + def test_getitem(self): t = self.forbid_delegation((5,3)) assert (t)[0] == 5 @@ -176,34 +208,25 @@ assert (t)[-1] == 3 assert (t)[-2] == 5 raises(IndexError, "t[2]") - + raises(IndexError, "t[-3]") + def test_three_tuples(self): - if not self.isspecialised((1,2,3)): - skip('3-tuples of ints are not specialised, so skip specific tests on them') - b = self.forbid_delegation((1,2,3)) + b = self.forbid_delegation((1, 2, 3)) c = (1,) - d = c + (2,3) + d = c + (2, 3) assert self.isspecialised(d) assert b == d - assert b <= d - + def test_mongrel(self): a = self.forbid_delegation((1, 2.2, '333')) - if not self.isspecialised(a): - skip('my chosen kind of mixed type tuple is not specialised, so skip specific tests on them') + assert self.isspecialised(a) assert len(a) == 3 assert a[0] == 1 and a[1] == 2.2 and a[2] == '333' - assert a == (1,) + (2.2,) + ('333',) - assert a < (1, 2.2, '334') - - def test_mongrel_with_any(self): - a = self.forbid_delegation((1, 2.2, '333',[])) - b = (1, 2.2) + ('333', []) - if not self.isspecialised(a): - skip('my chosen kind of mixed type tuple is not specialised, so skip specific tests on them') - assert len(a) == 4 - assert a[0] == 1 and a[1] == 2.2 and a[2] == '333' and a[3] == [] - assert a != (1, 2.2, '334', []) -# assert b == a -# assert a == (1,) + (2.2,) + ('333',) + ([],) -# assert a < (1, 2.2, '334', {}) + b = ('333',) + assert a == (1, 2.2,) + b + assert not a != (1, 2.2) + b + + +class AppTestAll(test_tupleobject.AppTestW_TupleObject): + def test_mul_identity(self): + skip("not working with specialisedtuple") diff --git a/pypy/objspace/std/test/test_tupleobject.py b/pypy/objspace/std/test/test_tupleobject.py --- a/pypy/objspace/std/test/test_tupleobject.py +++ b/pypy/objspace/std/test/test_tupleobject.py @@ -280,6 +280,8 @@ assert () * 10 == () assert (5,) * 3 == (5,5,5) assert (5,2) * 2 == (5,2,5,2) + + def test_mul_identity(self): t = (1,2,3) assert (t * 1) is t _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit