Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improve JIT performance by 1.4% on macOS Apple Silicon. Patch by Diego Russo.
21 changes: 18 additions & 3 deletions Python/jit.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,12 @@ jit_alloc(size_t size)
int failed = memory == NULL;
#else
int flags = MAP_ANONYMOUS | MAP_PRIVATE;
unsigned char *memory = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, -1, 0);
int prot = PROT_READ | PROT_WRITE;
# ifdef MAP_JIT
flags |= MAP_JIT;
prot |= PROT_EXEC;
# endif
unsigned char *memory = mmap(NULL, size, prot, flags, -1, 0);
int failed = memory == MAP_FAILED;
#endif
if (failed) {
Expand Down Expand Up @@ -102,8 +107,11 @@ mark_executable(unsigned char *memory, size_t size)
int old;
int failed = !VirtualProtect(memory, size, PAGE_EXECUTE_READ, &old);
#else
int failed = 0;
__builtin___clear_cache((char *)memory, (char *)memory + size);
int failed = mprotect(memory, size, PROT_EXEC | PROT_READ);
# ifndef MAP_JIT
failed = mprotect(memory, size, PROT_EXEC | PROT_READ);
# endif
#endif
if (failed) {
jit_error("unable to protect executable memory");
Expand Down Expand Up @@ -499,6 +507,9 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz
if (memory == NULL) {
return -1;
}
#ifdef MAP_JIT
pthread_jit_write_protect_np(0);
#endif
// Update the offsets of each instruction:
for (size_t i = 0; i < length; i++) {
state.instruction_starts[i] += (uintptr_t)memory;
Expand Down Expand Up @@ -529,7 +540,11 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz
data += group->data_size;
assert(code == memory + code_size);
assert(data == memory + code_size + data_size);
if (mark_executable(memory, total_size)) {
int status = mark_executable(memory, total_size);
#ifdef MAP_JIT
pthread_jit_write_protect_np(1);
#endif
if (status) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should work, right? Then we don't need status:

Suggested change
int status = mark_executable(memory, total_size);
#ifdef MAP_JIT
pthread_jit_write_protect_np(1);
#endif
if (status) {
#ifdef MAP_JIT
pthread_jit_write_protect_np(1);
#endif
if (mark_executable(memory, total_size)) {

Copy link
Contributor Author

@diegorusso diegorusso Nov 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is the same thing. if we place pthread_jit_write_protect_np(1) in mark_executable, this would be just after the clear cache. The solution you suggested would place it before the clear cache and It could raise an error as it enable the write protection and then the clear cache.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it matters which order they occur in (it's not like the cache invalidation tries to execute the code or anything). I just tried it locally and didn't experience any issues.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried not to change the previous code logic, but anyway you are right, it works in our internal CI as well. Unless there are other requests, I think this was the last request for change.

jit_free(memory, total_size);
return -1;
}
Expand Down
Loading