Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r79880:5348f162bc84 Date: 2015-09-28 13:22 +0200 http://bitbucket.org/pypy/pypy/changeset/5348f162bc84/
Log: Another performance boost to the common case of zip(). diff --git a/pypy/module/__builtin__/app_functional.py b/pypy/module/__builtin__/app_functional.py --- a/pypy/module/__builtin__/app_functional.py +++ b/pypy/module/__builtin__/app_functional.py @@ -5,6 +5,7 @@ from __future__ import with_statement import operator from __pypy__ import resizelist_hint, newlist_hint +from __pypy__ import specialized_zip_2_lists # ____________________________________________________________ @@ -217,11 +218,16 @@ in length to the length of the shortest argument sequence.""" l = len(sequences) if l == 2: + # A very fast path if the two sequences are lists + seq0 = sequences[0] + seq1 = sequences[1] + try: + return specialized_zip_2_lists(seq0, seq1) + except TypeError: + pass # This is functionally the same as the code below, but more # efficient because it unrolls the loops over 'sequences'. # Only for two arguments, which is the most common case. - seq0 = sequences[0] - seq1 = sequences[1] iter0 = iter(seq0) iter1 = iter(seq1) hint = min(100000000, # max 100M diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py --- a/pypy/module/__pypy__/__init__.py +++ b/pypy/module/__pypy__/__init__.py @@ -83,6 +83,7 @@ 'newdict' : 'interp_dict.newdict', 'reversed_dict' : 'interp_dict.reversed_dict', 'strategy' : 'interp_magic.strategy', # dict,set,list + 'specialized_zip_2_lists' : 'interp_magic.specialized_zip_2_lists', 'set_debug' : 'interp_magic.set_debug', 'locals_to_fast' : 'interp_magic.locals_to_fast', 'save_module_content_for_future_reload': diff --git a/pypy/module/__pypy__/interp_magic.py b/pypy/module/__pypy__/interp_magic.py --- a/pypy/module/__pypy__/interp_magic.py +++ b/pypy/module/__pypy__/interp_magic.py @@ -147,3 +147,7 @@ @unwrap_spec(w_module=MixedModule) def save_module_content_for_future_reload(space, w_module): w_module.save_module_content_for_future_reload() + +def specialized_zip_2_lists(space, w_list1, w_list2): + from pypy.objspace.std.specialisedtupleobject import specialized_zip_2_lists + return specialized_zip_2_lists(space, w_list1, w_list2) 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 @@ -1,7 +1,7 @@ from pypy.interpreter.error import OperationError from pypy.objspace.std.tupleobject import W_AbstractTupleObject from pypy.objspace.std.util import negate -from rpython.rlib.objectmodel import compute_hash +from rpython.rlib.objectmodel import compute_hash, specialize from rpython.rlib.rarithmetic import intmask from rpython.rlib.unroll import unrolling_iterable from rpython.tool.sourcetools import func_with_new_name @@ -146,3 +146,64 @@ return Cls_oo(space, w_arg1, w_arg2) else: raise NotSpecialised + +# -------------------------------------------------- +# Special code based on list strategies to implement zip(), +# here with two list arguments only. This builds a zipped +# list that differs from what the app-level code would build: +# if the source lists contain sometimes ints/floats and +# sometimes not, here we will use uniformly 'Cls_oo' instead +# of using 'Cls_ii' or 'Cls_ff' for the elements that match. +# This is a trade-off, but it looks like a good idea to keep +# the list uniform for the JIT---not to mention, it is much +# faster to move the decision out of the loop. + +@specialize.arg(1) +def _build_zipped_spec(space, Cls, lst1, lst2): + length = min(len(lst1), len(lst2)) + return [Cls(space, space.wrap(lst1[i]), + space.wrap(lst2[i])) for i in range(length)] + +def _build_zipped_spec_oo(space, w_list1, w_list2): + strat1 = w_list1.strategy + strat2 = w_list2.strategy + length = min(strat1.length(w_list1), strat2.length(w_list2)) + return [Cls_oo(space, strat1.getitem(w_list1, i), + strat2.getitem(w_list2, i)) for i in range(length)] + +def _build_zipped_unspec(space, w_list1, w_list2): + strat1 = w_list1.strategy + strat2 = w_list2.strategy + length = min(strat1.length(w_list1), strat2.length(w_list2)) + return [space.newtuple([strat1.getitem(w_list1, i), + strat2.getitem(w_list2, i)]) for i in range(length)] + +def specialized_zip_2_lists(space, w_list1, w_list2): + from pypy.objspace.std.listobject import W_ListObject + if (not isinstance(w_list1, W_ListObject) or + not isinstance(w_list2, W_ListObject)): + raise OperationError(space.w_TypeError, + space.wrap("expected two lists")) + + if space.config.objspace.std.withspecialisedtuple: + intlist1 = w_list1.getitems_int() + if intlist1 is not None: + intlist2 = w_list2.getitems_int() + if intlist2 is not None: + lst_w = _build_zipped_spec(space, Cls_ii, intlist1, intlist2) + return space.newlist(lst_w) + else: + floatlist1 = w_list1.getitems_float() + if floatlist1 is not None: + floatlist2 = w_list2.getitems_float() + if floatlist2 is not None: + lst_w = _build_zipped_spec(space, Cls_ff, floatlist1, + floatlist2) + return space.newlist(lst_w) + + lst_w = _build_zipped_spec_oo(space, w_list1, w_list2) + return space.newlist(lst_w) + + else: + lst_w = _build_zipped_unspec(space, w_list1, w_list2) + return space.newlist(lst_w) 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 @@ -407,3 +407,21 @@ assert (() != object()) is True assert ((1,) != object()) is True assert ((1, 2) != object()) is True + + def test_zip_two_lists(self): + try: + from __pypy__ import specialized_zip_2_lists + except ImportError: + specialized_zip_2_lists = zip + raises(TypeError, specialized_zip_2_lists, [], ()) + raises(TypeError, specialized_zip_2_lists, (), []) + assert specialized_zip_2_lists([], []) == [ + ] + assert specialized_zip_2_lists([2, 3], []) == [ + ] + assert specialized_zip_2_lists([2, 3], [4, 5, 6]) == [ + (2, 4), (3, 5)] + assert specialized_zip_2_lists([4.1, 3.6, 7.2], [2.3, 4.8]) == [ + (4.1, 2.3), (3.6, 4.8)] + assert specialized_zip_2_lists(["foo", "bar"], [6, 2]) == [ + ("foo", 6), ("bar", 2)] _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit