gh-109587: Allow "precompiled" perf-trampolines to largely mitigate the cost of enabling perf-trampolines (#109666)

This commit is contained in:
gsallam 2023-10-26 20:57:29 -07:00 committed by GitHub
parent 3d2f1f0b83
commit 21f068d80c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 199 additions and 10 deletions

View file

@ -193,7 +193,7 @@ typedef struct trampoline_api_st trampoline_api_t;
#define perf_code_arena _PyRuntime.ceval.perf.code_arena
#define trampoline_api _PyRuntime.ceval.perf.trampoline_api
#define perf_map_file _PyRuntime.ceval.perf.map_file
#define persist_after_fork _PyRuntime.ceval.perf.persist_after_fork
static void
perf_map_write_entry(void *state, const void *code_addr,
@ -361,6 +361,26 @@ default_eval:
}
#endif // PY_HAVE_PERF_TRAMPOLINE
int PyUnstable_PerfTrampoline_CompileCode(PyCodeObject *co)
{
#ifdef PY_HAVE_PERF_TRAMPOLINE
py_trampoline f = NULL;
assert(extra_code_index != -1);
int ret = _PyCode_GetExtra((PyObject *)co, extra_code_index, (void **)&f);
if (ret != 0 || f == NULL) {
py_trampoline new_trampoline = compile_trampoline();
if (new_trampoline == NULL) {
return 0;
}
trampoline_api.write_state(trampoline_api.state, new_trampoline,
perf_code_arena->code_size, co);
return _PyCode_SetExtra((PyObject *)co, extra_code_index,
(void *)new_trampoline);
}
#endif // PY_HAVE_PERF_TRAMPOLINE
return 0;
}
int
_PyIsPerfTrampolineActive(void)
{
@ -448,16 +468,34 @@ _PyPerfTrampoline_Fini(void)
return 0;
}
int
PyUnstable_PerfTrampoline_SetPersistAfterFork(int enable){
#ifdef PY_HAVE_PERF_TRAMPOLINE
persist_after_fork = enable;
return persist_after_fork;
#endif
return 0;
}
PyStatus
_PyPerfTrampoline_AfterFork_Child(void)
{
#ifdef PY_HAVE_PERF_TRAMPOLINE
// Restart trampoline in file in child.
int was_active = _PyIsPerfTrampolineActive();
_PyPerfTrampoline_Fini();
PyUnstable_PerfMapState_Fini();
if (was_active) {
_PyPerfTrampoline_Init(1);
if (persist_after_fork) {
char filename[256];
pid_t parent_pid = getppid();
snprintf(filename, sizeof(filename), "/tmp/perf-%d.map", parent_pid);
if (PyUnstable_CopyPerfMapFile(filename) != 0) {
return PyStatus_Error("Failed to copy perf map file.");
}
} else {
// Restart trampoline in file in child.
int was_active = _PyIsPerfTrampolineActive();
_PyPerfTrampoline_Fini();
if (was_active) {
_PyPerfTrampoline_Init(1);
}
}
#endif
return PyStatus_Ok();

View file

@ -2361,7 +2361,7 @@ PyAPI_FUNC(int) PyUnstable_WritePerfMapEntry(
#ifndef MS_WINDOWS
if (perf_map_state.perf_map == NULL) {
int ret = PyUnstable_PerfMapState_Init();
if(ret != 0){
if (ret != 0){
return ret;
}
}
@ -2388,6 +2388,45 @@ PyAPI_FUNC(void) PyUnstable_PerfMapState_Fini(void) {
#endif
}
PyAPI_FUNC(int) PyUnstable_CopyPerfMapFile(const char* parent_filename) {
#ifndef MS_WINDOWS
FILE* from = fopen(parent_filename, "r");
if (!from) {
return -1;
}
if (perf_map_state.perf_map == NULL) {
int ret = PyUnstable_PerfMapState_Init();
if (ret != 0) {
return ret;
}
}
char buf[4096];
PyThread_acquire_lock(perf_map_state.map_lock, 1);
int fflush_result = 0, result = 0;
while (1) {
size_t bytes_read = fread(buf, 1, sizeof(buf), from);
size_t bytes_written = fwrite(buf, 1, bytes_read, perf_map_state.perf_map);
fflush_result = fflush(perf_map_state.perf_map);
if (fflush_result != 0 || bytes_read == 0 || bytes_written < bytes_read) {
result = -1;
goto close_and_release;
}
if (bytes_read < sizeof(buf) && feof(from)) {
goto close_and_release;
}
}
close_and_release:
fclose(from);
PyThread_release_lock(perf_map_state.map_lock);
return result;
#endif
return 0;
}
#ifdef __cplusplus
}
#endif
static PyMethodDef sys_methods[] = {
/* Might as well keep this in alphabetic order */