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

Reply via email to