Skip to content

Commit

Permalink
pythonGH-88691: Shrink the CALL caches (pythonGH-103230)
Browse files Browse the repository at this point in the history
  • Loading branch information
brandtbucher authored Apr 5, 2023
1 parent aa5a9b5 commit b4978ff
Show file tree
Hide file tree
Showing 12 changed files with 285 additions and 281 deletions.
2 changes: 1 addition & 1 deletion Doc/library/dis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ the following command can be used to display the disassembly of
3 2 LOAD_GLOBAL 1 (NULL + len)
12 LOAD_FAST 0 (alist)
14 CALL 1
24 RETURN_VALUE
22 RETURN_VALUE

(The "2" is a line number).

Expand Down
1 change: 0 additions & 1 deletion Include/internal/pycore_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ typedef struct {
typedef struct {
uint16_t counter;
uint16_t func_version[2];
uint16_t min_args;
} _PyCallCache;

#define INLINE_CACHE_ENTRIES_CALL CACHE_ENTRIES(_PyCallCache)
Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ def _write_atomic(path, data, mode=0o666):
# Python 3.12a7 3522 (Removed JUMP_IF_FALSE_OR_POP/JUMP_IF_TRUE_OR_POP)
# Python 3.12a7 3523 (Convert COMPARE_AND_BRANCH back to COMPARE_OP)
# Python 3.12a7 3524 (Shrink the BINARY_SUBSCR caches)
# Python 3.12b1 3525 (Shrink the CALL caches)

# Python 3.13 will start with 3550

Expand All @@ -454,7 +455,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.

MAGIC_NUMBER = (3524).to_bytes(2, 'little') + b'\r\n'
MAGIC_NUMBER = (3525).to_bytes(2, 'little') + b'\r\n'

_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c

Expand Down
1 change: 0 additions & 1 deletion Lib/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,6 @@ def pseudo_op(name, op, real_ops):
"CALL": {
"counter": 1,
"func_version": 2,
"min_args": 1,
},
"STORE_SUBSCR": {
"counter": 1,
Expand Down
258 changes: 129 additions & 129 deletions Lib/test/test_dis.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Reduce the number of inline :opcode:`CACHE` entries for :opcode:`CALL`.
66 changes: 33 additions & 33 deletions Programs/test_frozenmain.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 24 additions & 19 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -2318,7 +2318,7 @@ dummy_func(
kwnames = GETITEM(frame->f_code->co_consts, oparg);
}

// Cache layout: counter/1, func_version/2, min_args/1
// Cache layout: counter/1, func_version/2
// Neither CALL_INTRINSIC_1/2 nor CALL_FUNCTION_EX are members!
family(call, INLINE_CACHE_ENTRIES_CALL) = {
CALL,
Expand Down Expand Up @@ -2348,7 +2348,7 @@ dummy_func(
// (Some args may be keywords, see KW_NAMES, which sets 'kwnames'.)
// On exit, the stack is [result].
// When calling Python, inline the call using DISPATCH_INLINED().
inst(CALL, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) {
inst(CALL, (unused/1, unused/2, method, callable, args[oparg] -- res)) {
int is_meth = method != NULL;
int total_args = oparg;
if (is_meth) {
Expand Down Expand Up @@ -2426,7 +2426,7 @@ dummy_func(
// Start out with [NULL, bound_method, arg1, arg2, ...]
// Transform to [callable, self, arg1, arg2, ...]
// Then fall through to CALL_PY_EXACT_ARGS
inst(CALL_BOUND_METHOD_EXACT_ARGS, (unused/1, unused/2, unused/1, method, callable, unused[oparg] -- unused)) {
inst(CALL_BOUND_METHOD_EXACT_ARGS, (unused/1, unused/2, method, callable, unused[oparg] -- unused)) {
DEOPT_IF(method != NULL, CALL);
DEOPT_IF(Py_TYPE(callable) != &PyMethod_Type, CALL);
STAT_INC(CALL, hit);
Expand All @@ -2438,7 +2438,7 @@ dummy_func(
GO_TO_INSTRUCTION(CALL_PY_EXACT_ARGS);
}

inst(CALL_PY_EXACT_ARGS, (unused/1, func_version/2, unused/1, method, callable, args[oparg] -- unused)) {
inst(CALL_PY_EXACT_ARGS, (unused/1, func_version/2, method, callable, args[oparg] -- unused)) {
assert(kwnames == NULL);
DEOPT_IF(tstate->interp->eval_frame, CALL);
int is_meth = method != NULL;
Expand All @@ -2465,7 +2465,7 @@ dummy_func(
DISPATCH_INLINED(new_frame);
}

inst(CALL_PY_WITH_DEFAULTS, (unused/1, func_version/2, min_args/1, method, callable, args[oparg] -- unused)) {
inst(CALL_PY_WITH_DEFAULTS, (unused/1, func_version/2, method, callable, args[oparg] -- unused)) {
assert(kwnames == NULL);
DEOPT_IF(tstate->interp->eval_frame, CALL);
int is_meth = method != NULL;
Expand All @@ -2479,6 +2479,11 @@ dummy_func(
PyFunctionObject *func = (PyFunctionObject *)callable;
DEOPT_IF(func->func_version != func_version, CALL);
PyCodeObject *code = (PyCodeObject *)func->func_code;
assert(func->func_defaults);
assert(PyTuple_CheckExact(func->func_defaults));
int defcount = (int)PyTuple_GET_SIZE(func->func_defaults);
assert(defcount <= code->co_argcount);
int min_args = code->co_argcount - defcount;
DEOPT_IF(argcount > code->co_argcount, CALL);
DEOPT_IF(argcount < min_args, CALL);
DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL);
Expand All @@ -2497,7 +2502,7 @@ dummy_func(
DISPATCH_INLINED(new_frame);
}

inst(CALL_NO_KW_TYPE_1, (unused/1, unused/2, unused/1, null, callable, args[oparg] -- res)) {
inst(CALL_NO_KW_TYPE_1, (unused/1, unused/2, null, callable, args[oparg] -- res)) {
assert(kwnames == NULL);
assert(cframe.use_tracing == 0);
assert(oparg == 1);
Expand All @@ -2510,7 +2515,7 @@ dummy_func(
Py_DECREF(&PyType_Type); // I.e., callable
}

inst(CALL_NO_KW_STR_1, (unused/1, unused/2, unused/1, null, callable, args[oparg] -- res)) {
inst(CALL_NO_KW_STR_1, (unused/1, unused/2, null, callable, args[oparg] -- res)) {
assert(kwnames == NULL);
assert(cframe.use_tracing == 0);
assert(oparg == 1);
Expand All @@ -2525,7 +2530,7 @@ dummy_func(
CHECK_EVAL_BREAKER();
}

inst(CALL_NO_KW_TUPLE_1, (unused/1, unused/2, unused/1, null, callable, args[oparg] -- res)) {
inst(CALL_NO_KW_TUPLE_1, (unused/1, unused/2, null, callable, args[oparg] -- res)) {
assert(kwnames == NULL);
assert(oparg == 1);
DEOPT_IF(null != NULL, CALL);
Expand All @@ -2539,7 +2544,7 @@ dummy_func(
CHECK_EVAL_BREAKER();
}

inst(CALL_BUILTIN_CLASS, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) {
inst(CALL_BUILTIN_CLASS, (unused/1, unused/2, method, callable, args[oparg] -- res)) {
int is_meth = method != NULL;
int total_args = oparg;
if (is_meth) {
Expand All @@ -2564,7 +2569,7 @@ dummy_func(
CHECK_EVAL_BREAKER();
}

inst(CALL_NO_KW_BUILTIN_O, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) {
inst(CALL_NO_KW_BUILTIN_O, (unused/1, unused/2, method, callable, args[oparg] -- res)) {
assert(cframe.use_tracing == 0);
/* Builtin METH_O functions */
assert(kwnames == NULL);
Expand Down Expand Up @@ -2596,7 +2601,7 @@ dummy_func(
CHECK_EVAL_BREAKER();
}

inst(CALL_NO_KW_BUILTIN_FAST, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) {
inst(CALL_NO_KW_BUILTIN_FAST, (unused/1, unused/2, method, callable, args[oparg] -- res)) {
assert(cframe.use_tracing == 0);
/* Builtin METH_FASTCALL functions, without keywords */
assert(kwnames == NULL);
Expand Down Expand Up @@ -2632,7 +2637,7 @@ dummy_func(
CHECK_EVAL_BREAKER();
}

inst(CALL_BUILTIN_FAST_WITH_KEYWORDS, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) {
inst(CALL_BUILTIN_FAST_WITH_KEYWORDS, (unused/1, unused/2, method, callable, args[oparg] -- res)) {
assert(cframe.use_tracing == 0);
/* Builtin METH_FASTCALL | METH_KEYWORDS functions */
int is_meth = method != NULL;
Expand Down Expand Up @@ -2668,7 +2673,7 @@ dummy_func(
CHECK_EVAL_BREAKER();
}

inst(CALL_NO_KW_LEN, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) {
inst(CALL_NO_KW_LEN, (unused/1, unused/2, method, callable, args[oparg] -- res)) {
assert(cframe.use_tracing == 0);
assert(kwnames == NULL);
/* len(o) */
Expand Down Expand Up @@ -2696,7 +2701,7 @@ dummy_func(
ERROR_IF(res == NULL, error);
}

inst(CALL_NO_KW_ISINSTANCE, (unused/1, unused/2, unused/1, method, callable, args[oparg] -- res)) {
inst(CALL_NO_KW_ISINSTANCE, (unused/1, unused/2, method, callable, args[oparg] -- res)) {
assert(cframe.use_tracing == 0);
assert(kwnames == NULL);
/* isinstance(o, o2) */
Expand Down Expand Up @@ -2727,7 +2732,7 @@ dummy_func(
}

// This is secretly a super-instruction
inst(CALL_NO_KW_LIST_APPEND, (unused/1, unused/2, unused/1, method, self, args[oparg] -- unused)) {
inst(CALL_NO_KW_LIST_APPEND, (unused/1, unused/2, method, self, args[oparg] -- unused)) {
assert(cframe.use_tracing == 0);
assert(kwnames == NULL);
assert(oparg == 1);
Expand All @@ -2748,7 +2753,7 @@ dummy_func(
DISPATCH();
}

inst(CALL_NO_KW_METHOD_DESCRIPTOR_O, (unused/1, unused/2, unused/1, method, unused, args[oparg] -- res)) {
inst(CALL_NO_KW_METHOD_DESCRIPTOR_O, (unused/1, unused/2, method, unused, args[oparg] -- res)) {
assert(kwnames == NULL);
int is_meth = method != NULL;
int total_args = oparg;
Expand Down Expand Up @@ -2782,7 +2787,7 @@ dummy_func(
CHECK_EVAL_BREAKER();
}

inst(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, (unused/1, unused/2, unused/1, method, unused, args[oparg] -- res)) {
inst(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, (unused/1, unused/2, method, unused, args[oparg] -- res)) {
int is_meth = method != NULL;
int total_args = oparg;
if (is_meth) {
Expand Down Expand Up @@ -2814,7 +2819,7 @@ dummy_func(
CHECK_EVAL_BREAKER();
}

inst(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS, (unused/1, unused/2, unused/1, method, unused, args[oparg] -- res)) {
inst(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS, (unused/1, unused/2, method, unused, args[oparg] -- res)) {
assert(kwnames == NULL);
assert(oparg == 0 || oparg == 1);
int is_meth = method != NULL;
Expand Down Expand Up @@ -2846,7 +2851,7 @@ dummy_func(
CHECK_EVAL_BREAKER();
}

inst(CALL_NO_KW_METHOD_DESCRIPTOR_FAST, (unused/1, unused/2, unused/1, method, unused, args[oparg] -- res)) {
inst(CALL_NO_KW_METHOD_DESCRIPTOR_FAST, (unused/1, unused/2, method, unused, args[oparg] -- res)) {
assert(kwnames == NULL);
int is_meth = method != NULL;
int total_args = oparg;
Expand Down
Loading

0 comments on commit b4978ff

Please sign in to comment.