Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-123516: Improve JIT memory consumption by invalidating cold executors #123402

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
0eac77b
resolve conflict
savannahostrowski Jul 26, 2024
d576296
tests pass except ssl
savannahostrowski Jun 25, 2024
68e95d6
remove file
savannahostrowski Jul 26, 2024
c903af4
this is broken
savannahostrowski Aug 12, 2024
5ca6b7f
gc approach
savannahostrowski Aug 12, 2024
beb4f65
rebase
savannahostrowski Aug 12, 2024
427dbf5
Update has_run to run_count
savannahostrowski Aug 13, 2024
92d5590
update initialized run_count and move invalidate old
savannahostrowski Aug 13, 2024
0cdf638
set threshold to 1`
savannahostrowski Aug 13, 2024
58e7447
move incrementing run count into a new op
savannahostrowski Aug 14, 2024
6c047e4
add invalidation threshold in gc of 10
savannahostrowski Aug 20, 2024
2645023
move back to incremenet
savannahostrowski Aug 20, 2024
7c7ae98
remove print
savannahostrowski Aug 20, 2024
6d6d306
move invalidation to executor creation
savannahostrowski Aug 27, 2024
4d086fe
change threshold
savannahostrowski Aug 27, 2024
d08e45a
new line
savannahostrowski Aug 27, 2024
6315877
update constant
savannahostrowski Aug 27, 2024
622c266
📜🤖 Added by blurb_it.
blurb-it[bot] Aug 27, 2024
e5117b2
Merge branch 'main' into jit-mem-invalidate-10
savannahostrowski Aug 29, 2024
7c6704c
resolve conflict
savannahostrowski Jul 26, 2024
1d72fdd
tests pass except ssl
savannahostrowski Jun 25, 2024
1778185
remove file
savannahostrowski Jul 26, 2024
2506821
this is broken
savannahostrowski Aug 12, 2024
fca6dec
gc approach
savannahostrowski Aug 12, 2024
29436fd
rebase
savannahostrowski Aug 12, 2024
b969b11
Update has_run to run_count
savannahostrowski Aug 13, 2024
a669e0f
update initialized run_count and move invalidate old
savannahostrowski Aug 13, 2024
deb73ec
set threshold to 1`
savannahostrowski Aug 13, 2024
9fa55e8
move incrementing run count into a new op
savannahostrowski Aug 14, 2024
e4a461a
add invalidation threshold in gc of 10
savannahostrowski Aug 20, 2024
d5a2bed
move back to incremenet
savannahostrowski Aug 20, 2024
d232e63
remove print
savannahostrowski Aug 20, 2024
e4a456a
move invalidation to executor creation
savannahostrowski Aug 27, 2024
b7d2d5a
change threshold
savannahostrowski Aug 27, 2024
6dcd2dc
new line
savannahostrowski Aug 27, 2024
219f890
update constant
savannahostrowski Aug 27, 2024
fb7b04d
📜🤖 Added by blurb_it.
blurb-it[bot] Aug 27, 2024
ea397a3
Merge branch 'jit-mem-invalidate-10' of https://github.com/savannahos…
savannahostrowski Aug 29, 2024
310d20c
address pr comments
savannahostrowski Aug 30, 2024
d2f9dc4
refactor invalidatecold
savannahostrowski Aug 30, 2024
d755d56
refactor to was_run bool
savannahostrowski Aug 30, 2024
c9534c0
update blurb
savannahostrowski Aug 30, 2024
61cd3c5
Merge branch 'main' into jit-mem-invalidate-10
savannahostrowski Aug 30, 2024
e5065ad
Update op name to be more reflective of was_run
savannahostrowski Aug 30, 2024
2d09259
fix typo
savannahostrowski Aug 30, 2024
d2e8e29
rename uop
savannahostrowski Aug 30, 2024
a894598
dedent and initialize executors_created
savannahostrowski Aug 31, 2024
758ee03
Merge branch 'main' into jit-mem-invalidate-10
savannahostrowski Sep 1, 2024
1927bfe
address some PR comments
savannahostrowski Sep 1, 2024
8ee0d7f
Update Python/optimizer.c
savannahostrowski Sep 1, 2024
cedd65d
Update Python/optimizer.c
savannahostrowski Sep 1, 2024
0a9b5b6
make was_run uint`
savannahostrowski Sep 1, 2024
180a68e
add comment for JIT_CLEANUP_THRESHOLD
savannahostrowski Sep 1, 2024
7cb9cba
Remove extraneous reset of executors_created
savannahostrowski Sep 1, 2024
8939ecf
Merge branch 'main' into jit-mem-invalidate-10
savannahostrowski Sep 3, 2024
00f03d2
Merge branch 'main' into jit-mem-invalidate-10
savannahostrowski Sep 3, 2024
4981ab2
Merge branch 'jit-mem-invalidate-10' of https://github.com/savannahos…
savannahostrowski Sep 3, 2024
3c59316
condense conditional statements
savannahostrowski Sep 3, 2024
306c3c3
Merge branch 'main' into jit-mem-invalidate-10
savannahostrowski Sep 10, 2024
fe50615
Address PR comments from Brandt and Mark
savannahostrowski Sep 13, 2024
d51817b
Merge branch 'main' into jit-mem-invalidate-10
savannahostrowski Sep 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Include/internal/pycore_interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,8 @@ struct _is {
struct callable_cache callable_cache;
_PyOptimizerObject *optimizer;
_PyExecutorObject *executor_list_head;

// new_executors is limited to JIT_CLEANUP_THRESHOLD
size_t new_executors;
_rare_events rare_events;
PyDict_WatchCallback builtins_dict_watcher;

Expand Down
11 changes: 10 additions & 1 deletion Include/internal/pycore_optimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ typedef struct {
uint8_t oparg;
uint16_t valid:1;
uint16_t linked:1;
uint16_t chain_depth:14; // Must be big engough for MAX_CHAIN_DEPTH - 1.
uint16_t chain_depth:6; // Must be big enough for MAX_CHAIN_DEPTH - 1.
bool warm;
int index; // Index of ENTER_EXECUTOR (if code isn't NULL, below).
_PyBloomFilter bloom;
_PyExecutorLinkListNode links;
Expand Down Expand Up @@ -123,11 +124,19 @@ PyAPI_FUNC(PyObject *) _PyOptimizer_NewUOpOptimizer(void);
#ifdef _Py_TIER2
PyAPI_FUNC(void) _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is_invalidation);
PyAPI_FUNC(void) _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation);
PyAPI_FUNC(void) _Py_Executors_InvalidateCold(PyInterpreterState *interp);

#else
# define _Py_Executors_InvalidateDependency(A, B, C) ((void)0)
# define _Py_Executors_InvalidateAll(A, B) ((void)0)
# define _Py_Executors_InvalidateCold(A) ((void)0)

#endif

// When new executors are created, we check to see if the number of
// executors created meets or exceeds the JIT_CLEANUP_THRESHOLD. If it
// does, we invalidate cold executors.
#define JIT_CLEANUP_THRESHOLD 10
savannahostrowski marked this conversation as resolved.
Show resolved Hide resolved

// This is the length of the trace we project initially.
#define UOP_MAX_TRACE_LENGTH 800
Expand Down
71 changes: 36 additions & 35 deletions Include/internal/pycore_uop_ids.h

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

4 changes: 4 additions & 0 deletions Include/internal/pycore_uop_metadata.h

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

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Improved JIT memory consumption by periodically freeing memory used by infrequently-executed code.
This change is especially likely to improve the memory footprint of long-running programs.
4 changes: 4 additions & 0 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -4831,6 +4831,10 @@ dummy_func(
assert(((_PyExecutorObject *)executor)->vm_data.valid);
}

tier2 op(_MAKE_WARM, (--)) {
current_executor->vm_data.warm = true;
}

tier2 op(_FATAL_ERROR, (--)) {
assert(0);
Py_FatalError("Fatal error uop executed.");
Expand Down
5 changes: 5 additions & 0 deletions Python/executor_cases.c.h

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

48 changes: 48 additions & 0 deletions Python/optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@ _PyOptimizer_Optimize(
if (err <= 0) {
return err;
}

if (++interp->new_executors >= JIT_CLEANUP_THRESHOLD) {
interp->new_executors = 0;
_Py_Executors_InvalidateCold(interp);
}

assert(*executor_ptr != NULL);
if (progress_needed) {
int index = get_index_for_executor(code, start);
Expand Down Expand Up @@ -565,6 +571,7 @@ translate_bytecode_to_trace(
code->co_firstlineno,
2 * INSTR_IP(initial_instr, code));
ADD_TO_TRACE(_START_EXECUTOR, 0, (uintptr_t)instr, INSTR_IP(instr, code));
ADD_TO_TRACE(_MAKE_WARM, 0, 0, 0);
uint32_t target = 0;

for (;;) {
Expand Down Expand Up @@ -1194,6 +1201,9 @@ make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFil
executor->jit_code = NULL;
executor->jit_side_entry = NULL;
executor->jit_size = 0;
// This is initialized to true so we can prevent the executor
// from being immediately detected as cold and invalidated.
executor->vm_data.warm = true;
if (_PyJIT_Compile(executor, executor->trace, length)) {
Py_DECREF(executor);
return NULL;
Expand Down Expand Up @@ -1659,4 +1669,42 @@ _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation)
}
}

void
_Py_Executors_InvalidateCold(PyInterpreterState *interp)
{
savannahostrowski marked this conversation as resolved.
Show resolved Hide resolved
/* Walk the list of executors */
/* TO DO -- Use a tree to avoid traversing as many objects */
PyObject *invalidate = PyList_New(0);
if (invalidate == NULL) {
goto error;
}

/* Clearing an executor can deallocate others, so we need to make a list of
* executors to invalidate first */
for (_PyExecutorObject *exec = interp->executor_list_head; exec != NULL;) {
assert(exec->vm_data.valid);
_PyExecutorObject *next = exec->vm_data.links.next;
savannahostrowski marked this conversation as resolved.
Show resolved Hide resolved

if (!exec->vm_data.warm && PyList_Append(invalidate, (PyObject *)exec) < 0) {
goto error;
}
else {
exec->vm_data.warm = false;
}

exec = next;
}
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(invalidate); i++) {
_PyExecutorObject *exec = (_PyExecutorObject *)PyList_GET_ITEM(invalidate, i);
executor_clear(exec);
}
Py_DECREF(invalidate);
return;
savannahostrowski marked this conversation as resolved.
Show resolved Hide resolved
error:
PyErr_Clear();
Py_XDECREF(invalidate);
// If we're truly out of memory, wiping out everything is a fine fallback:
_Py_Executors_InvalidateAll(interp, 0);
}

#endif /* _Py_TIER2 */
4 changes: 4 additions & 0 deletions Python/optimizer_cases.c.h

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

1 change: 1 addition & 0 deletions Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,7 @@ init_interpreter(PyInterpreterState *interp,
#ifdef _Py_TIER2
(void)_Py_SetOptimizer(interp, NULL);
interp->executor_list_head = NULL;
interp->new_executors = 0;
#endif
if (interp != &runtime->_main_interpreter) {
/* Fix the self-referential, statically initialized fields. */
Expand Down
Loading