Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a164574

Browse files
committed
Transform "x in (1,2,3)" to "x in frozenset([1,2,3])".
Inspired by Skip's idea to recognize the throw-away nature of sequences in this context and to transform their type to one with better performance.
1 parent dbecd93 commit a164574

2 files changed

Lines changed: 58 additions & 1 deletion

File tree

Lib/test/test_peepholer.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,16 @@ def test_folding_of_binops_on_constants(self):
133133
asm = dis_single('a="x"*1000')
134134
self.assert_('(1000)' in asm)
135135

136+
def test_set_conversion(self):
137+
for line in (
138+
'x in (1,2,3)',
139+
'x not in (1,2,3)',
140+
'not x in (1,2,3)',
141+
'not x not in (1,2,3)',
142+
):
143+
asm = dis_single(line)
144+
self.assert_('frozenset' in asm)
145+
136146
def test_elim_extra_return(self):
137147
# RETURN LOAD_CONST None RETURN --> RETURN
138148
def f(x):

Python/compile.c

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,47 @@ fold_binops_on_constants(unsigned char *codestr, PyObject *consts)
540540
return 1;
541541
}
542542

543+
/* Replace LOAD_CONST tuple with LOAD_CONST frozenset in the context
544+
of a single-use constant for "in" and "not in" tests.
545+
*/
546+
int
547+
try_set_conversion(unsigned char *codestr, PyObject *consts)
548+
{
549+
PyObject *newconst, *constant;
550+
int arg, len_consts;
551+
552+
/* Pre-conditions */
553+
assert(PyList_CheckExact(consts));
554+
assert(codestr[0] == LOAD_CONST);
555+
assert(codestr[3] == COMPARE_OP);
556+
assert(GETARG(codestr, 3) == 6 || GETARG(codestr, 3) == 7);
557+
558+
/* Attempt to convert constant to a frozenset. Bail-out with no
559+
changes if the tuple contains unhashable values. */
560+
arg = GETARG(codestr, 0);
561+
constant = PyList_GET_ITEM(consts, arg);
562+
if (constant->ob_type != &PyTuple_Type)
563+
return 0;
564+
newconst = PyObject_CallFunctionObjArgs(
565+
(PyObject *)&PyFrozenSet_Type, constant, NULL);
566+
if (newconst == NULL) {
567+
PyErr_Clear();
568+
return 0;
569+
}
570+
571+
/* Append new constant onto consts list.*/
572+
len_consts = PyList_GET_SIZE(consts);
573+
if (PyList_Append(consts, newconst)) {
574+
Py_DECREF(newconst);
575+
return 0;
576+
}
577+
Py_DECREF(newconst);
578+
579+
/* Write new LOAD_CONST newconst on top of LOAD_CONST oldconst */
580+
SETARG(codestr, 0, len_consts);
581+
return 1;
582+
}
583+
543584
static unsigned int *
544585
markblocks(unsigned char *code, int len)
545586
{
@@ -665,9 +706,15 @@ optimize_code(PyObject *code, PyObject* consts, PyObject *names, PyObject *linen
665706
/* not a is b --> a is not b
666707
not a in b --> a not in b
667708
not a is not b --> a is b
668-
not a not in b --> a in b */
709+
not a not in b --> a in b
710+
711+
a in c --> a in frozenset(c)
712+
where c is a constant tuple of hashable values
713+
*/
669714
case COMPARE_OP:
670715
j = GETARG(codestr, i);
716+
if (lastlc >= 1 && (j == 6 || j == 7) && ISBASICBLOCK(blocks,i-3,6))
717+
try_set_conversion(&codestr[i-3], consts);
671718
if (j < 6 || j > 9 ||
672719
codestr[i+3] != UNARY_NOT ||
673720
!ISBASICBLOCK(blocks,i,4))

0 commit comments

Comments
 (0)