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.
This commit is contained in:
Serhiy Storchaka 2018-03-11 10:54:47 +02:00 committed by GitHub
parent 4e2442505c
commit 3f7e9aa2ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 44 additions and 2 deletions

View File

@ -3,6 +3,19 @@ import unittest
from test.bytecode_helper import BytecodeTestCase 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): class TestTranforms(BytecodeTestCase):
def test_unot(self): def test_unot(self):
@ -311,6 +324,17 @@ class TestTranforms(BytecodeTestCase):
self.assertFalse(instr.opname.startswith('BINARY_')) self.assertFalse(instr.opname.startswith('BINARY_'))
self.assertFalse(instr.opname.startswith('BUILD_')) 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): class TestBuglets(unittest.TestCase):

View File

@ -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.

View File

@ -369,7 +369,8 @@ fold_subscr(expr_ty node, PyArena *arena, int optimize)
} }
/* Change literal list or set of constants into constant /* 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 Used for right operand of "in" and "not in" tests and for iterable
in "for" loop and comprehensions. in "for" loop and comprehensions.
*/ */
@ -378,7 +379,21 @@ fold_iter(expr_ty arg, PyArena *arena, int optimize)
{ {
PyObject *newval; PyObject *newval;
if (arg->kind == List_kind) { 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) { else if (arg->kind == Set_kind) {
newval = make_const_tuple(arg->v.Set.elts); newval = make_const_tuple(arg->v.Set.elts);