Skip to content

Commit c5d6922

Browse files
committed
Use a per-interpreter callable cache
1 parent ba214fc commit c5d6922

File tree

7 files changed

+41
-44
lines changed

7 files changed

+41
-44
lines changed

Include/internal/pycore_code.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ extern "C" {
1010

1111

1212
// Inline caches. If you change the number of cache entries for an instruction,
13-
// you must *also* bump the magic number in Lib/importlib/_bootstap_external.py!
13+
// you must *also* update the number of cache entries in Lib/opcode.py and bump
14+
// the magic number in Lib/importlib/_bootstap_external.py!
1415

1516
#define CACHE_ENTRIES(cache) (sizeof(cache)/sizeof(_Py_CODEUNIT))
1617

@@ -113,9 +114,12 @@ _Py_IncrementCountAndMaybeQuicken(PyCodeObject *code)
113114

114115
extern Py_ssize_t _Py_QuickenedCount;
115116

116-
extern PyObject *builtin_isinstance;
117-
extern PyObject *builtin_len;
118-
extern PyObject *builtin_list_append;
117+
// Borrowed references to common callables:
118+
struct callable_cache {
119+
PyObject *isinstance;
120+
PyObject *len;
121+
PyObject *list_append;
122+
};
119123

120124
/* "Locals plus" for a code object is the set of locals + cell vars +
121125
* free vars. This relates to variable names as well as offsets into
@@ -258,8 +262,7 @@ extern int _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub, _Py_CO
258262
extern int _Py_Specialize_Call(PyObject *callable, _Py_CODEUNIT *instr,
259263
int nargs, PyObject *kwnames);
260264
extern int _Py_Specialize_Precall(PyObject *callable, _Py_CODEUNIT *instr,
261-
int nargs, PyObject *kwnames,
262-
PyObject *builtins, int oparg);
265+
int nargs, PyObject *kwnames, int oparg);
263266
extern void _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
264267
int oparg);
265268
extern void _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs,

Include/internal/pycore_global_strings.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ struct _Py_global_strings {
269269
STRUCT_FOR_ID(inf)
270270
STRUCT_FOR_ID(intersection)
271271
STRUCT_FOR_ID(isatty)
272+
STRUCT_FOR_ID(isinstance)
272273
STRUCT_FOR_ID(items)
273274
STRUCT_FOR_ID(iter)
274275
STRUCT_FOR_ID(join)
@@ -278,6 +279,7 @@ struct _Py_global_strings {
278279
STRUCT_FOR_ID(last_type)
279280
STRUCT_FOR_ID(last_value)
280281
STRUCT_FOR_ID(latin1)
282+
STRUCT_FOR_ID(len)
281283
STRUCT_FOR_ID(line)
282284
STRUCT_FOR_ID(lineno)
283285
STRUCT_FOR_ID(listcomp)

Include/internal/pycore_interp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ extern "C" {
1212

1313
#include "pycore_atomic.h" // _Py_atomic_address
1414
#include "pycore_ast_state.h" // struct ast_state
15+
#include "pycore_code.h" // struct callable_cache
1516
#include "pycore_context.h" // struct _Py_context_state
1617
#include "pycore_dict.h" // struct _Py_dict_state
1718
#include "pycore_exceptions.h" // struct _Py_exc_state
@@ -176,6 +177,7 @@ struct _is {
176177

177178
struct ast_state ast;
178179
struct type_cache type_cache;
180+
struct callable_cache callable_cache;
179181

180182
/* The following fields are here to avoid allocation during init.
181183
The data is exposed through PyInterpreterState pointer fields.

Include/internal/pycore_runtime_init.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,7 @@ extern "C" {
884884
INIT_ID(inf), \
885885
INIT_ID(intersection), \
886886
INIT_ID(isatty), \
887+
INIT_ID(isinstance), \
887888
INIT_ID(items), \
888889
INIT_ID(iter), \
889890
INIT_ID(join), \
@@ -893,6 +894,7 @@ extern "C" {
893894
INIT_ID(last_type), \
894895
INIT_ID(last_value), \
895896
INIT_ID(latin1), \
897+
INIT_ID(len), \
896898
INIT_ID(line), \
897899
INIT_ID(lineno), \
898900
INIT_ID(listcomp), \

Python/ceval.c

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4569,14 +4569,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
45694569
/* Move ownership of reference from stack to call_shape
45704570
* and make sure that NULL is cleared from stack */
45714571
PyObject *function = PEEK(nargs + 1);
4572-
#ifdef Py_STATS
4573-
extern int _PySpecialization_ClassifyCallable(PyObject *);
4574-
SpecializationStats *stats =
4575-
&_py_stats.opcode_stats[PRECALL].specialization;
4576-
stats->failure++;
4577-
int kind = _PySpecialization_ClassifyCallable(function);
4578-
stats->failure_kinds[kind]++;
4579-
#endif
45804572
if (!is_method && Py_TYPE(function) == &PyMethod_Type) {
45814573
PyObject *meth = ((PyMethodObject *)function)->im_func;
45824574
PyObject *self = ((PyMethodObject *)function)->im_self;
@@ -4694,8 +4686,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
46944686
int nargs = oparg + is_meth;
46954687
PyObject *callable = PEEK(nargs + 1);
46964688
int err = _Py_Specialize_Precall(callable, next_instr, nargs,
4697-
call_shape.kwnames,
4698-
BUILTINS(), oparg);
4689+
call_shape.kwnames, oparg);
46994690
if (err < 0) {
47004691
goto error;
47014692
}
@@ -5011,7 +5002,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
50115002
int total_args = oparg + is_meth;
50125003
DEOPT_IF(total_args != 1, PRECALL);
50135004
PyObject *callable = PEEK(total_args + 1);
5014-
DEOPT_IF(callable != builtin_len, PRECALL);
5005+
PyInterpreterState *interp = _PyInterpreterState_GET();
5006+
DEOPT_IF(callable != interp->callable_cache.len, PRECALL);
50155007
STAT_INC(PRECALL, hit);
50165008
SKIP_CALL();
50175009
PyObject *arg = TOP();
@@ -5040,8 +5032,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
50405032
int total_args = oparg + is_meth;
50415033
PyObject *callable = PEEK(total_args + 1);
50425034
DEOPT_IF(total_args != 2, PRECALL);
5043-
5044-
DEOPT_IF(callable != builtin_isinstance, PRECALL);
5035+
PyInterpreterState *interp = _PyInterpreterState_GET();
5036+
DEOPT_IF(callable != interp->callable_cache.isinstance, PRECALL);
50455037
STAT_INC(PRECALL, hit);
50465038
SKIP_CALL();
50475039
PyObject *cls = POP();
@@ -5070,8 +5062,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
50705062
assert(call_shape.kwnames == NULL);
50715063
assert(oparg == 1);
50725064
PyObject *callable = PEEK(3);
5073-
assert(builtin_list_append);
5074-
DEOPT_IF(callable != builtin_list_append, PRECALL);
5065+
PyInterpreterState *interp = _PyInterpreterState_GET();
5066+
DEOPT_IF(callable != interp->callable_cache.list_append, PRECALL);
50755067
PyObject *list = SECOND();
50765068
DEOPT_IF(!PyList_Check(list), PRECALL);
50775069
STAT_INC(PRECALL, hit);

Python/pylifecycle.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,16 @@ pycore_init_builtins(PyThreadState *tstate)
774774
Py_INCREF(builtins_dict);
775775
interp->builtins = builtins_dict;
776776

777+
PyObject *isinstance = PyDict_GetItem(builtins_dict, &_Py_ID(isinstance));
778+
assert(isinstance);
779+
interp->callable_cache.isinstance = isinstance;
780+
PyObject *len = PyDict_GetItem(builtins_dict, &_Py_ID(len));
781+
assert(len);
782+
interp->callable_cache.len = len;
783+
PyObject *list_append = _PyType_Lookup(&PyList_Type, &_Py_ID(append));
784+
assert(list_append);
785+
interp->callable_cache.list_append = list_append;
786+
777787
if (_PyBuiltins_AddExceptions(bimod) < 0) {
778788
return _PyStatus_ERR("failed to add exceptions to builtins");
779789
}

Python/specialize.c

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,7 +1577,7 @@ specialize_py_call(PyFunctionObject *func, _Py_CODEUNIT *instr, int nargs,
15771577

15781578
static int
15791579
specialize_c_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs,
1580-
PyObject *kwnames, PyObject *builtins)
1580+
PyObject *kwnames)
15811581
{
15821582
assert(_Py_OPCODE(*instr) == PRECALL_ADAPTIVE);
15831583
if (PyCFunction_GET_FUNCTION(callable) == NULL) {
@@ -1596,12 +1596,8 @@ specialize_c_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs,
15961596
return 1;
15971597
}
15981598
/* len(o) */
1599-
if (builtin_len == NULL) {
1600-
// Use builtins_copy to protect against mutated builtins:
1601-
builtin_len = PyDict_GetItemString(
1602-
_PyInterpreterState_GET()->builtins_copy, "len");
1603-
}
1604-
if (callable == builtin_len) {
1599+
PyInterpreterState *interp = _PyInterpreterState_GET();
1600+
if (callable == interp->callable_cache.len) {
16051601
*instr = _Py_MAKECODEUNIT(PRECALL_NO_KW_LEN,
16061602
_Py_OPARG(*instr));
16071603
return 0;
@@ -1617,12 +1613,8 @@ specialize_c_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs,
16171613
}
16181614
if (nargs == 2) {
16191615
/* isinstance(o1, o2) */
1620-
if (builtin_isinstance == NULL) {
1621-
// Use builtins_copy to protect against mutated builtins:
1622-
builtin_isinstance = PyDict_GetItemString(
1623-
_PyInterpreterState_GET()->builtins_copy, "isinstance");
1624-
}
1625-
if (callable == builtin_isinstance) {
1616+
PyInterpreterState *interp = _PyInterpreterState_GET();
1617+
if (callable == interp->callable_cache.isinstance) {
16261618
*instr = _Py_MAKECODEUNIT(PRECALL_NO_KW_ISINSTANCE,
16271619
_Py_OPARG(*instr));
16281620
return 0;
@@ -1688,14 +1680,14 @@ call_fail_kind(PyObject *callable)
16881680

16891681
int
16901682
_Py_Specialize_Precall(PyObject *callable, _Py_CODEUNIT *instr, int nargs,
1691-
PyObject *kwnames, PyObject *builtins, int oparg)
1683+
PyObject *kwnames, int oparg)
16921684
{
16931685
assert(_PyOpcode_InlineCacheEntries[PRECALL] ==
16941686
INLINE_CACHE_ENTRIES_PRECALL);
16951687
_PyPrecallCache *cache = (_PyPrecallCache *)(instr + 1);
16961688
int fail;
16971689
if (PyCFunction_CheckExact(callable)) {
1698-
fail = specialize_c_call(callable, instr, nargs, kwnames, builtins);
1690+
fail = specialize_c_call(callable, instr, nargs, kwnames);
16991691
}
17001692
else if (PyFunction_Check(callable)) {
17011693
*instr = _Py_MAKECODEUNIT(PRECALL_PYFUNC, _Py_OPARG(*instr));
@@ -1717,12 +1709,12 @@ _Py_Specialize_Precall(PyObject *callable, _Py_CODEUNIT *instr, int nargs,
17171709
fail = -1;
17181710
}
17191711
if (fail) {
1720-
STAT_INC(CALL, failure);
1712+
STAT_INC(PRECALL, failure);
17211713
assert(!PyErr_Occurred());
17221714
cache->counter = ADAPTIVE_CACHE_BACKOFF;
17231715
}
17241716
else {
1725-
STAT_INC(CALL, success);
1717+
STAT_INC(PRECALL, success);
17261718
assert(!PyErr_Occurred());
17271719
cache->counter = initial_counter_value();
17281720
}
@@ -2132,10 +2124,4 @@ int
21322124
return SPEC_FAIL_OTHER;
21332125
}
21342126

2135-
int
2136-
_PySpecialization_ClassifyCallable(PyObject *callable)
2137-
{
2138-
return call_fail_kind(callable);
2139-
}
2140-
21412127
#endif

0 commit comments

Comments
 (0)