diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2018-03-11 08:54:47 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-11 08:54:47 (GMT) |
commit | 3f7e9aa2ef215917b9f1521441f67f4ecd33a1bc (patch) | |
tree | 7f4b2918d822c863869ef604332e851eeea5c0a1 | |
parent | 4e2442505c5e9eec396dcef4d2e6bdd2b6f92fc9 (diff) | |
download | cpython-3f7e9aa2ef215917b9f1521441f67f4ecd33a1bc.zip cpython-3f7e9aa2ef215917b9f1521441f67f4ecd33a1bc.tar.gz cpython-3f7e9aa2ef215917b9f1521441f67f4ecd33a1bc.tar.bz2 |
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.
-rw-r--r-- | Lib/test/test_peepholer.py | 24 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2018-02-24-00-07-05.bpo-32925.e-7Ufh.rst | 3 | ||||
-rw-r--r-- | Python/ast_opt.c | 19 |
3 files changed, 44 insertions, 2 deletions
diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index c24cf77..794d104 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -3,6 +3,19 @@ import unittest 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 @@ class TestTranforms(BytecodeTestCase): 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 0000000..e9443e6 --- /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 65cf3c1..a54f98c 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); |