Skip to content

Commit 424ecab

Browse files
authored
bpo-46841: Use inline caching for UNPACK_SEQUENCE (pythonGH-31591)
1 parent c32aef4 commit 424ecab

File tree

7 files changed

+39
-29
lines changed

7 files changed

+39
-29
lines changed

Include/internal/pycore_code.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,16 @@ typedef struct {
6868
_Py_CODEUNIT counter;
6969
} _PyBinaryOpCache;
7070

71+
typedef struct {
72+
_Py_CODEUNIT counter;
73+
} _PyUnpackSequenceCache;
74+
7175
#define INLINE_CACHE_ENTRIES_BINARY_OP \
7276
(sizeof(_PyBinaryOpCache) / sizeof(_Py_CODEUNIT))
7377

78+
#define INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE \
79+
(sizeof(_PyUnpackSequenceCache) / sizeof(_Py_CODEUNIT))
80+
7481
/* Maximum size of code to quicken, in code units. */
7582
#define MAX_SIZE_TO_QUICKEN 5000
7683

@@ -312,7 +319,7 @@ extern void _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *
312319
int oparg);
313320
extern void _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr, SpecializedCacheEntry *cache);
314321
extern void _Py_Specialize_UnpackSequence(PyObject *seq, _Py_CODEUNIT *instr,
315-
SpecializedCacheEntry *cache);
322+
int oparg);
316323

317324
/* Deallocator function for static codeobjects used in deepfreeze.py */
318325
extern void _PyStaticCode_Dealloc(PyCodeObject *co);

Include/opcode.h

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

Lib/importlib/_bootstrap_external.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ def _write_atomic(path, data, mode=0o666):
388388
# Python 3.11a5 3479 (Add PUSH_NULL opcode)
389389
# Python 3.11a5 3480 (New CALL opcodes, second iteration)
390390
# Python 3.11a5 3481 (Use inline CACHE instructions)
391+
# Python 3.11a5 3482 (Use inline caching for UNPACK_SEQUENCE)
391392

392393
# Python 3.12 will start with magic number 3500
393394

@@ -402,7 +403,7 @@ def _write_atomic(path, data, mode=0o666):
402403
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
403404
# in PC/launcher.c must also be updated.
404405

405-
MAGIC_NUMBER = (3481).to_bytes(2, 'little') + b'\r\n'
406+
MAGIC_NUMBER = (3482).to_bytes(2, 'little') + b'\r\n'
406407
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
407408

408409
_PYCACHE = '__pycache__'

Lib/opcode.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ def jabs_op(name, op, entries=0):
109109

110110
name_op('STORE_NAME', 90) # Index in name list
111111
name_op('DELETE_NAME', 91) # ""
112-
def_op('UNPACK_SEQUENCE', 92) # Number of tuple items
112+
def_op('UNPACK_SEQUENCE', 92, 1) # Number of tuple items
113113
jrel_op('FOR_ITER', 93)
114114
def_op('UNPACK_EX', 94)
115115
name_op('STORE_ATTR', 95) # Index in name list
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Use inline caching for :opcode:`UNPACK_SEQUENCE`.

Python/ceval.c

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2758,22 +2758,22 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
27582758
}
27592759
STACK_GROW(oparg);
27602760
Py_DECREF(seq);
2761+
JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE);
27612762
DISPATCH();
27622763
}
27632764

27642765
TARGET(UNPACK_SEQUENCE_ADAPTIVE) {
27652766
assert(cframe.use_tracing == 0);
2766-
SpecializedCacheEntry *cache = GET_CACHE();
2767-
if (cache->adaptive.counter == 0) {
2767+
_PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr;
2768+
if (cache->counter == 0) {
27682769
PyObject *seq = TOP();
27692770
next_instr--;
2770-
_Py_Specialize_UnpackSequence(seq, next_instr, cache);
2771+
_Py_Specialize_UnpackSequence(seq, next_instr, oparg);
27712772
DISPATCH();
27722773
}
27732774
else {
27742775
STAT_INC(UNPACK_SEQUENCE, deferred);
2775-
cache->adaptive.counter--;
2776-
oparg = cache->adaptive.original_oparg;
2776+
cache->counter--;
27772777
JUMP_TO_INSTRUCTION(UNPACK_SEQUENCE);
27782778
}
27792779
}
@@ -2786,36 +2786,37 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
27862786
SET_TOP(Py_NewRef(PyTuple_GET_ITEM(seq, 1)));
27872787
PUSH(Py_NewRef(PyTuple_GET_ITEM(seq, 0)));
27882788
Py_DECREF(seq);
2789+
JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE);
27892790
NOTRACE_DISPATCH();
27902791
}
27912792

27922793
TARGET(UNPACK_SEQUENCE_TUPLE) {
27932794
PyObject *seq = TOP();
2794-
int len = GET_CACHE()->adaptive.original_oparg;
27952795
DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE);
2796-
DEOPT_IF(PyTuple_GET_SIZE(seq) != len, UNPACK_SEQUENCE);
2796+
DEOPT_IF(PyTuple_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE);
27972797
STAT_INC(UNPACK_SEQUENCE, hit);
27982798
STACK_SHRINK(1);
27992799
PyObject **items = _PyTuple_ITEMS(seq);
2800-
while (len--) {
2801-
PUSH(Py_NewRef(items[len]));
2800+
while (oparg--) {
2801+
PUSH(Py_NewRef(items[oparg]));
28022802
}
28032803
Py_DECREF(seq);
2804+
JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE);
28042805
NOTRACE_DISPATCH();
28052806
}
28062807

28072808
TARGET(UNPACK_SEQUENCE_LIST) {
28082809
PyObject *seq = TOP();
2809-
int len = GET_CACHE()->adaptive.original_oparg;
28102810
DEOPT_IF(!PyList_CheckExact(seq), UNPACK_SEQUENCE);
2811-
DEOPT_IF(PyList_GET_SIZE(seq) != len, UNPACK_SEQUENCE);
2811+
DEOPT_IF(PyList_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE);
28122812
STAT_INC(UNPACK_SEQUENCE, hit);
28132813
STACK_SHRINK(1);
28142814
PyObject **items = _PyList_ITEMS(seq);
2815-
while (len--) {
2816-
PUSH(Py_NewRef(items[len]));
2815+
while (oparg--) {
2816+
PUSH(Py_NewRef(items[oparg]));
28172817
}
28182818
Py_DECREF(seq);
2819+
JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE);
28192820
NOTRACE_DISPATCH();
28202821
}
28212822

@@ -5600,7 +5601,7 @@ MISS_WITH_CACHE(CALL)
56005601
MISS_WITH_INLINE_CACHE(BINARY_OP)
56015602
MISS_WITH_CACHE(COMPARE_OP)
56025603
MISS_WITH_CACHE(BINARY_SUBSCR)
5603-
MISS_WITH_CACHE(UNPACK_SEQUENCE)
5604+
MISS_WITH_INLINE_CACHE(UNPACK_SEQUENCE)
56045605
MISS_WITH_OPARG_COUNTER(STORE_SUBSCR)
56055606

56065607
LOAD_ATTR_INSTANCE_VALUE_miss:

Python/specialize.c

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ static uint8_t cache_requirements[256] = {
6767
[PRECALL] = 2, /* _PyAdaptiveEntry and _PyObjectCache/_PyCallCache */
6868
[STORE_ATTR] = 1, // _PyAdaptiveEntry
6969
[COMPARE_OP] = 1, /* _PyAdaptiveEntry */
70-
[UNPACK_SEQUENCE] = 1, // _PyAdaptiveEntry
7170
};
7271

7372
Py_ssize_t _Py_QuickenedCount = 0;
@@ -2133,39 +2132,39 @@ unpack_sequence_fail_kind(PyObject *seq)
21332132
#endif
21342133

21352134
void
2136-
_Py_Specialize_UnpackSequence(PyObject *seq, _Py_CODEUNIT *instr,
2137-
SpecializedCacheEntry *cache)
2135+
_Py_Specialize_UnpackSequence(PyObject *seq, _Py_CODEUNIT *instr, int oparg)
21382136
{
2139-
_PyAdaptiveEntry *adaptive = &cache->adaptive;
2137+
assert(_PyOpcode_InlineCacheEntries[UNPACK_SEQUENCE] ==
2138+
INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE);
2139+
_PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)(instr + 1);
21402140
if (PyTuple_CheckExact(seq)) {
2141-
if (PyTuple_GET_SIZE(seq) != adaptive->original_oparg) {
2141+
if (PyTuple_GET_SIZE(seq) != oparg) {
21422142
SPECIALIZATION_FAIL(UNPACK_SEQUENCE, SPEC_FAIL_EXPECTED_ERROR);
21432143
goto failure;
21442144
}
21452145
if (PyTuple_GET_SIZE(seq) == 2) {
2146-
*instr = _Py_MAKECODEUNIT(UNPACK_SEQUENCE_TWO_TUPLE,
2147-
_Py_OPARG(*instr));
2146+
*instr = _Py_MAKECODEUNIT(UNPACK_SEQUENCE_TWO_TUPLE, oparg);
21482147
goto success;
21492148
}
2150-
*instr = _Py_MAKECODEUNIT(UNPACK_SEQUENCE_TUPLE, _Py_OPARG(*instr));
2149+
*instr = _Py_MAKECODEUNIT(UNPACK_SEQUENCE_TUPLE, oparg);
21512150
goto success;
21522151
}
21532152
if (PyList_CheckExact(seq)) {
2154-
if (PyList_GET_SIZE(seq) != adaptive->original_oparg) {
2153+
if (PyList_GET_SIZE(seq) != oparg) {
21552154
SPECIALIZATION_FAIL(UNPACK_SEQUENCE, SPEC_FAIL_EXPECTED_ERROR);
21562155
goto failure;
21572156
}
2158-
*instr = _Py_MAKECODEUNIT(UNPACK_SEQUENCE_LIST, _Py_OPARG(*instr));
2157+
*instr = _Py_MAKECODEUNIT(UNPACK_SEQUENCE_LIST, oparg);
21592158
goto success;
21602159
}
21612160
SPECIALIZATION_FAIL(UNPACK_SEQUENCE, unpack_sequence_fail_kind(seq));
21622161
failure:
21632162
STAT_INC(UNPACK_SEQUENCE, failure);
2164-
cache_backoff(adaptive);
2163+
cache->counter = ADAPTIVE_CACHE_BACKOFF;
21652164
return;
21662165
success:
21672166
STAT_INC(UNPACK_SEQUENCE, success);
2168-
adaptive->counter = initial_counter_value();
2167+
cache->counter = initial_counter_value();
21692168
}
21702169

21712170
#ifdef Py_STATS

0 commit comments

Comments
 (0)