[Python-checkins] bpo-32925: Optimized iterating and containing test for literal lists (GH-5842)

Serhiy Storchaka webhook-mailer at python.org
Sun Mar 11 04:54:50 EDT 2018


https://github.com/python/cpython/commit/3f7e9aa2ef215917b9f1521441f67f4ecd33a1bc
commit: 3f7e9aa2ef215917b9f1521441f67f4ecd33a1bc
branch: master
author: Serhiy Storchaka <storchaka at gmail.com>
committer: GitHub <noreply at github.com>
date: 2018-03-11T10:54:47+02:00
summary:

bpo-32925: Optimized iterating and containing test for literal lists (GH-5842)

consisting of non-constants: `x in [a, b]` and `for x in [a, b]`.
The case of all constant elements already was optimized.

files:
A Misc/NEWS.d/next/Core and Builtins/2018-02-24-00-07-05.bpo-32925.e-7Ufh.rst
M Lib/test/test_peepholer.py
M Python/ast_opt.c

diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py
index c24cf77e7b2d..794d104d5919 100644
--- a/Lib/test/test_peepholer.py
+++ b/Lib/test/test_peepholer.py
@@ -3,6 +3,19 @@
 
 from test.bytecode_helper import BytecodeTestCase
 
+def count_instr_recursively(f, opname):
+    count = 0
+    for instr in dis.get_instructions(f):
+        if instr.opname == opname:
+            count += 1
+    if hasattr(f, '__code__'):
+        f = f.__code__
+    for c in f.co_consts:
+        if hasattr(c, 'co_code'):
+            count += count_instr_recursively(c, opname)
+    return count
+
+
 class TestTranforms(BytecodeTestCase):
 
     def test_unot(self):
@@ -311,6 +324,17 @@ def test_constant_folding(self):
                 self.assertFalse(instr.opname.startswith('BINARY_'))
                 self.assertFalse(instr.opname.startswith('BUILD_'))
 
+    def test_in_literal_list(self):
+        def containtest():
+            return x in [a, b]
+        self.assertEqual(count_instr_recursively(containtest, 'BUILD_LIST'), 0)
+
+    def test_iterate_literal_list(self):
+        def forloop():
+            for x in [a, b]:
+                pass
+        self.assertEqual(count_instr_recursively(forloop, 'BUILD_LIST'), 0)
+
 
 class TestBuglets(unittest.TestCase):
 
diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-02-24-00-07-05.bpo-32925.e-7Ufh.rst b/Misc/NEWS.d/next/Core and Builtins/2018-02-24-00-07-05.bpo-32925.e-7Ufh.rst
new file mode 100644
index 000000000000..e9443e69e2a2
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2018-02-24-00-07-05.bpo-32925.e-7Ufh.rst	
@@ -0,0 +1,3 @@
+Optimized iterating and containing test for literal lists consisting of
+non-constants: ``x in [a, b]`` and ``for x in [a, b]``. The case of all
+constant elements already was optimized.
diff --git a/Python/ast_opt.c b/Python/ast_opt.c
index 65cf3c126417..a54f98c8c721 100644
--- a/Python/ast_opt.c
+++ b/Python/ast_opt.c
@@ -369,7 +369,8 @@ fold_subscr(expr_ty node, PyArena *arena, int optimize)
 }
 
 /* Change literal list or set of constants into constant
-   tuple or frozenset respectively.
+   tuple or frozenset respectively.  Change literal list of
+   non-constants into tuple.
    Used for right operand of "in" and "not in" tests and for iterable
    in "for" loop and comprehensions.
 */
@@ -378,7 +379,21 @@ fold_iter(expr_ty arg, PyArena *arena, int optimize)
 {
     PyObject *newval;
     if (arg->kind == List_kind) {
-        newval = make_const_tuple(arg->v.List.elts);
+        /* First change a list into tuple. */
+        asdl_seq *elts = arg->v.List.elts;
+        Py_ssize_t n = asdl_seq_LEN(elts);
+        for (Py_ssize_t i = 0; i < n; i++) {
+            expr_ty e = (expr_ty)asdl_seq_GET(elts, i);
+            if (e->kind == Starred_kind) {
+                return 1;
+            }
+        }
+        expr_context_ty ctx = arg->v.List.ctx;
+        arg->kind = Tuple_kind;
+        arg->v.Tuple.elts = elts;
+        arg->v.Tuple.ctx = ctx;
+        /* Try to create a constant tuple. */
+        newval = make_const_tuple(elts);
     }
     else if (arg->kind == Set_kind) {
         newval = make_const_tuple(arg->v.Set.elts);



More information about the Python-checkins mailing list