Skip to content

Commit a9da085

Browse files
authored
bpo-46702: Specialize UNPACK_SEQUENCE (GH-31240)
1 parent e8a19b0 commit a9da085

File tree

7 files changed

+149
-84
lines changed

7 files changed

+149
-84
lines changed

Include/internal/pycore_code.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,8 @@ int _Py_Specialize_CallNoKw(PyObject *callable, _Py_CODEUNIT *instr, int nargs,
276276
void _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
277277
SpecializedCacheEntry *cache);
278278
void _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, SpecializedCacheEntry *cache);
279+
void _Py_Specialize_UnpackSequence(PyObject *seq, _Py_CODEUNIT *instr,
280+
SpecializedCacheEntry *cache);
279281

280282
/* Deallocator function for static codeobjects used in deepfreeze.py */
281283
void _PyStaticCode_Dealloc(PyCodeObject *co);

Include/opcode.h

Lines changed: 9 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/opcode.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,10 @@ def jabs_op(name, op):
283283
"STORE_ATTR_INSTANCE_VALUE",
284284
"STORE_ATTR_SLOT",
285285
"STORE_ATTR_WITH_HINT",
286+
"UNPACK_SEQUENCE_ADAPTIVE",
287+
"UNPACK_SEQUENCE_LIST",
288+
"UNPACK_SEQUENCE_TUPLE",
289+
"UNPACK_SEQUENCE_TWO_TUPLE",
286290
# Super instructions
287291
"LOAD_FAST__LOAD_FAST",
288292
"STORE_FAST__LOAD_FAST",
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Specialize :opcode:`UNPACK_SEQUENCE` for :class:`tuple` and :class:`list`
2+
unpackings.

Python/ceval.c

Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2738,52 +2738,84 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
27382738

27392739
TARGET(UNPACK_SEQUENCE) {
27402740
PREDICTED(UNPACK_SEQUENCE);
2741-
PyObject *seq = POP(), *item, **items;
2742-
#ifdef Py_STATS
2743-
extern int _PySpecialization_ClassifySequence(PyObject *, int);
2744-
_py_stats.opcode_stats[UNPACK_SEQUENCE].specialization.failure++;
2745-
_py_stats.opcode_stats[UNPACK_SEQUENCE].specialization.
2746-
failure_kinds[_PySpecialization_ClassifySequence(seq, oparg)]++;
2747-
#endif
2748-
if (PyTuple_CheckExact(seq) &&
2749-
PyTuple_GET_SIZE(seq) == oparg) {
2750-
items = ((PyTupleObject *)seq)->ob_item;
2751-
while (oparg--) {
2752-
item = items[oparg];
2753-
Py_INCREF(item);
2754-
PUSH(item);
2755-
}
2756-
} else if (PyList_CheckExact(seq) &&
2757-
PyList_GET_SIZE(seq) == oparg) {
2758-
items = ((PyListObject *)seq)->ob_item;
2759-
while (oparg--) {
2760-
item = items[oparg];
2761-
Py_INCREF(item);
2762-
PUSH(item);
2763-
}
2764-
} else if (unpack_iterable(tstate, seq, oparg, -1,
2765-
stack_pointer + oparg)) {
2766-
STACK_GROW(oparg);
2767-
} else {
2768-
/* unpack_iterable() raised an exception */
2741+
PyObject *seq = POP();
2742+
PyObject **top = stack_pointer + oparg;
2743+
if (!unpack_iterable(tstate, seq, oparg, -1, top)) {
27692744
Py_DECREF(seq);
27702745
goto error;
27712746
}
2747+
STACK_GROW(oparg);
27722748
Py_DECREF(seq);
27732749
DISPATCH();
27742750
}
27752751

2752+
TARGET(UNPACK_SEQUENCE_ADAPTIVE) {
2753+
assert(cframe.use_tracing == 0);
2754+
SpecializedCacheEntry *cache = GET_CACHE();
2755+
if (cache->adaptive.counter == 0) {
2756+
PyObject *seq = TOP();
2757+
next_instr--;
2758+
_Py_Specialize_UnpackSequence(seq, next_instr, cache);
2759+
DISPATCH();
2760+
}
2761+
else {
2762+
STAT_INC(UNPACK_SEQUENCE, deferred);
2763+
cache->adaptive.counter--;
2764+
oparg = cache->adaptive.original_oparg;
2765+
JUMP_TO_INSTRUCTION(UNPACK_SEQUENCE);
2766+
}
2767+
}
2768+
2769+
TARGET(UNPACK_SEQUENCE_TWO_TUPLE) {
2770+
PyObject *seq = TOP();
2771+
DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE);
2772+
DEOPT_IF(PyTuple_GET_SIZE(seq) != 2, UNPACK_SEQUENCE);
2773+
STAT_INC(UNPACK_SEQUENCE, hit);
2774+
SET_TOP(Py_NewRef(PyTuple_GET_ITEM(seq, 1)));
2775+
PUSH(Py_NewRef(PyTuple_GET_ITEM(seq, 0)));
2776+
Py_DECREF(seq);
2777+
NOTRACE_DISPATCH();
2778+
}
2779+
2780+
TARGET(UNPACK_SEQUENCE_TUPLE) {
2781+
PyObject *seq = TOP();
2782+
int len = GET_CACHE()->adaptive.original_oparg;
2783+
DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE);
2784+
DEOPT_IF(PyTuple_GET_SIZE(seq) != len, UNPACK_SEQUENCE);
2785+
STAT_INC(UNPACK_SEQUENCE, hit);
2786+
STACK_SHRINK(1);
2787+
PyObject **items = _PyTuple_ITEMS(seq);
2788+
while (len--) {
2789+
PUSH(Py_NewRef(items[len]));
2790+
}
2791+
Py_DECREF(seq);
2792+
NOTRACE_DISPATCH();
2793+
}
2794+
2795+
TARGET(UNPACK_SEQUENCE_LIST) {
2796+
PyObject *seq = TOP();
2797+
int len = GET_CACHE()->adaptive.original_oparg;
2798+
DEOPT_IF(!PyList_CheckExact(seq), UNPACK_SEQUENCE);
2799+
DEOPT_IF(PyList_GET_SIZE(seq) != len, UNPACK_SEQUENCE);
2800+
STAT_INC(UNPACK_SEQUENCE, hit);
2801+
STACK_SHRINK(1);
2802+
PyObject **items = _PyList_ITEMS(seq);
2803+
while (len--) {
2804+
PUSH(Py_NewRef(items[len]));
2805+
}
2806+
Py_DECREF(seq);
2807+
NOTRACE_DISPATCH();
2808+
}
2809+
27762810
TARGET(UNPACK_EX) {
27772811
int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8);
27782812
PyObject *seq = POP();
2779-
2780-
if (unpack_iterable(tstate, seq, oparg & 0xFF, oparg >> 8,
2781-
stack_pointer + totalargs)) {
2782-
stack_pointer += totalargs;
2783-
} else {
2813+
PyObject **top = stack_pointer + totalargs;
2814+
if (!unpack_iterable(tstate, seq, oparg & 0xFF, oparg >> 8, top)) {
27842815
Py_DECREF(seq);
27852816
goto error;
27862817
}
2818+
STACK_GROW(totalargs);
27872819
Py_DECREF(seq);
27882820
DISPATCH();
27892821
}
@@ -5396,6 +5428,7 @@ MISS_WITH_CACHE(CALL)
53965428
MISS_WITH_CACHE(BINARY_OP)
53975429
MISS_WITH_CACHE(COMPARE_OP)
53985430
MISS_WITH_CACHE(BINARY_SUBSCR)
5431+
MISS_WITH_CACHE(UNPACK_SEQUENCE)
53995432
MISS_WITH_OPARG_COUNTER(STORE_SUBSCR)
54005433

54015434
binary_subscr_dict_error:

Python/opcode_targets.h

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/specialize.c

Lines changed: 57 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ static uint8_t adaptive_opcodes[256] = {
5151
[STORE_ATTR] = STORE_ATTR_ADAPTIVE,
5252
[BINARY_OP] = BINARY_OP_ADAPTIVE,
5353
[COMPARE_OP] = COMPARE_OP_ADAPTIVE,
54+
[UNPACK_SEQUENCE] = UNPACK_SEQUENCE_ADAPTIVE,
5455
};
5556

5657
/* The number of cache entries required for a "family" of instructions. */
@@ -64,6 +65,7 @@ static uint8_t cache_requirements[256] = {
6465
[STORE_ATTR] = 2, /* _PyAdaptiveEntry and _PyAttrCache */
6566
[BINARY_OP] = 1, // _PyAdaptiveEntry
6667
[COMPARE_OP] = 1, /* _PyAdaptiveEntry */
68+
[UNPACK_SEQUENCE] = 1, // _PyAdaptiveEntry
6769
};
6870

6971
Py_ssize_t _Py_QuickenedCount = 0;
@@ -155,6 +157,7 @@ _Py_GetSpecializationStats(void) {
155157
err += add_stat_dict(stats, CALL, "call");
156158
err += add_stat_dict(stats, BINARY_OP, "binary_op");
157159
err += add_stat_dict(stats, COMPARE_OP, "compare_op");
160+
err += add_stat_dict(stats, UNPACK_SEQUENCE, "unpack_sequence");
158161
if (err < 0) {
159162
Py_DECREF(stats);
160163
return NULL;
@@ -607,27 +610,10 @@ initial_counter_value(void) {
607610
#define SPEC_FAIL_FOR_ITER_DICT_VALUES 22
608611
#define SPEC_FAIL_FOR_ITER_ENUMERATE 23
609612

610-
/* UNPACK_SEQUENCE */
611-
#define SPEC_FAIL_UNPACK_SEQUENCE_TUPLE_0 9
612-
#define SPEC_FAIL_UNPACK_SEQUENCE_TUPLE_1 10
613-
#define SPEC_FAIL_UNPACK_SEQUENCE_TUPLE_2 11
614-
#define SPEC_FAIL_UNPACK_SEQUENCE_TUPLE_3 12
615-
#define SPEC_FAIL_UNPACK_SEQUENCE_TUPLE_4 13
616-
#define SPEC_FAIL_UNPACK_SEQUENCE_TUPLE_N 14
617-
618-
#define SPEC_FAIL_UNPACK_SEQUENCE_LIST_0 15
619-
#define SPEC_FAIL_UNPACK_SEQUENCE_LIST_1 16
620-
#define SPEC_FAIL_UNPACK_SEQUENCE_LIST_2 17
621-
#define SPEC_FAIL_UNPACK_SEQUENCE_LIST_3 18
622-
#define SPEC_FAIL_UNPACK_SEQUENCE_LIST_4 19
623-
#define SPEC_FAIL_UNPACK_SEQUENCE_LIST_N 20
624-
625-
#define SPEC_FAIL_UNPACK_SEQUENCE_OTHER_0 21
626-
#define SPEC_FAIL_UNPACK_SEQUENCE_OTHER_1 22
627-
#define SPEC_FAIL_UNPACK_SEQUENCE_OTHER_2 23
628-
#define SPEC_FAIL_UNPACK_SEQUENCE_OTHER_3 24
629-
#define SPEC_FAIL_UNPACK_SEQUENCE_OTHER_4 25
630-
#define SPEC_FAIL_UNPACK_SEQUENCE_OTHER_N 26
613+
// UNPACK_SEQUENCE
614+
615+
#define SPEC_FAIL_UNPACK_SEQUENCE_ITERATOR 8
616+
#define SPEC_FAIL_UNPACK_SEQUENCE_SEQUENCE 9
631617

632618

633619
static int
@@ -1949,6 +1935,56 @@ _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs,
19491935
adaptive->counter = initial_counter_value();
19501936
}
19511937

1938+
#ifdef Py_STATS
1939+
static int
1940+
unpack_sequence_fail_kind(PyObject *seq)
1941+
{
1942+
if (PySequence_Check(seq)) {
1943+
return SPEC_FAIL_UNPACK_SEQUENCE_SEQUENCE;
1944+
}
1945+
if (PyIter_Check(seq)) {
1946+
return SPEC_FAIL_UNPACK_SEQUENCE_ITERATOR;
1947+
}
1948+
return SPEC_FAIL_OTHER;
1949+
}
1950+
#endif
1951+
1952+
void
1953+
_Py_Specialize_UnpackSequence(PyObject *seq, _Py_CODEUNIT *instr,
1954+
SpecializedCacheEntry *cache)
1955+
{
1956+
_PyAdaptiveEntry *adaptive = &cache->adaptive;
1957+
if (PyTuple_CheckExact(seq)) {
1958+
if (PyTuple_GET_SIZE(seq) != adaptive->original_oparg) {
1959+
SPECIALIZATION_FAIL(UNPACK_SEQUENCE, SPEC_FAIL_EXPECTED_ERROR);
1960+
goto failure;
1961+
}
1962+
if (PyTuple_GET_SIZE(seq) == 2) {
1963+
*instr = _Py_MAKECODEUNIT(UNPACK_SEQUENCE_TWO_TUPLE,
1964+
_Py_OPARG(*instr));
1965+
goto success;
1966+
}
1967+
*instr = _Py_MAKECODEUNIT(UNPACK_SEQUENCE_TUPLE, _Py_OPARG(*instr));
1968+
goto success;
1969+
}
1970+
if (PyList_CheckExact(seq)) {
1971+
if (PyList_GET_SIZE(seq) != adaptive->original_oparg) {
1972+
SPECIALIZATION_FAIL(UNPACK_SEQUENCE, SPEC_FAIL_EXPECTED_ERROR);
1973+
goto failure;
1974+
}
1975+
*instr = _Py_MAKECODEUNIT(UNPACK_SEQUENCE_LIST, _Py_OPARG(*instr));
1976+
goto success;
1977+
}
1978+
SPECIALIZATION_FAIL(UNPACK_SEQUENCE, unpack_sequence_fail_kind(seq));
1979+
failure:
1980+
STAT_INC(UNPACK_SEQUENCE, failure);
1981+
cache_backoff(adaptive);
1982+
return;
1983+
success:
1984+
STAT_INC(UNPACK_SEQUENCE, success);
1985+
adaptive->counter = initial_counter_value();
1986+
}
1987+
19521988
#ifdef Py_STATS
19531989

19541990
int
@@ -2001,22 +2037,6 @@ int
20012037
return SPEC_FAIL_OTHER;
20022038
}
20032039

2004-
int
2005-
_PySpecialization_ClassifySequence(PyObject *seq, int n)
2006-
{
2007-
assert(n >= 0);
2008-
if (n > 4) {
2009-
n = 5;
2010-
}
2011-
if (PyTuple_CheckExact(seq)) {
2012-
return SPEC_FAIL_UNPACK_SEQUENCE_TUPLE_0 + n;
2013-
}
2014-
if (PyList_CheckExact(seq)) {
2015-
return SPEC_FAIL_UNPACK_SEQUENCE_LIST_0 + n;
2016-
}
2017-
return SPEC_FAIL_UNPACK_SEQUENCE_OTHER_0 + n;
2018-
}
2019-
20202040
int
20212041
_PySpecialization_ClassifyCallable(PyObject *callable)
20222042
{

0 commit comments

Comments
 (0)