Has this happened in time for b1?
Yep, all of these build options (and the environment variable) are available on the tagged release: gh-118335: Configure Tier 2 interpreter at build time by gvanrossum · Pull Request #118339 · python/cpython (github.com)
This is as far as I got in the Fedora package by adding --enable-experimental-jit=yes-off:
...
python3.13 /builddir/build/BUILD/Python-3.13.0b1/Tools/jit/build.py x86_64-redhat-linux-gnu --debug
==============================================================
JIT support for x86_64-redhat-linux-gnu is still experimental!
Please report any issues you encounter.
==============================================================
python3.13 /builddir/build/BUILD/Python-3.13.0b1/Tools/jit/build.py x86_64-redhat-linux-gnu --debug
==============================================================
JIT support for x86_64-redhat-linux-gnu is still experimental!
Please report any issues you encounter.
==============================================================
gcc -c -fno-strict-overflow -Wsign-compare -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -DDYNAMIC_ANNOTATIONS_ENABLED=1 -fexceptions -fcf-protection -fexceptions -fcf-protection -fexceptions -fcf-protection -O0 -Wno-cpp -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Wno-complain-wrong-lang -Werror=format-security -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -march=x86-64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -mtls-dialect=gnu2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -D_GNU_SOURCE -fPIC -fwrapv -D_Py_TIER2=3 -D_Py_JIT -flto -fuse-linker-plugin -ffat-lto-objects -flto-partition=none -g -std=c11 -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wstrict-prototypes -Werror=implicit-function-declaration -fvisibility=hidden -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Wno-complain-wrong-lang -Werror=format-security -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -march=x86-64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -mtls-dialect=gnu2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -D_GNU_SOURCE -fPIC -fwrapv -O0 -Wno-cpp -I/builddir/build/BUILD/Python-3.13.0b1/Include/internal -I/builddir/build/BUILD/Python-3.13.0b1/Include/internal/mimalloc -IObjects -IInclude -IPython -I. -I/builddir/build/BUILD/Python-3.13.0b1/Include -fPIC -DPy_BUILD_CORE -o Python/jit.o /builddir/build/BUILD/Python-3.13.0b1/Python/jit.c
In file included from /builddir/build/BUILD/Python-3.13.0b1/Python/jit.c:392:
./jit_stencils.h: In function ‘emit__DELETE_DEREF’:
./jit_stencils.h:15433:39: error: ‘_PyCriticalSection_BeginSlow’ undeclared (first use in this function)
15433 | patch_64(data + 0xf0, (uintptr_t)&_PyCriticalSection_BeginSlow);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
./jit_stencils.h:15433:39: note: each undeclared identifier is reported only once for each function it appears in
./jit_stencils.h:15435:40: error: ‘_PyCriticalSection_Resume’ undeclared (first use in this function)
15435 | patch_64(data + 0x100, (uintptr_t)&_PyCriticalSection_Resume);
| ^~~~~~~~~~~~~~~~~~~~~~~~~
./jit_stencils.h: In function ‘emit__LOAD_DEREF’:
./jit_stencils.h:29402:39: error: ‘_PyCriticalSection_BeginSlow’ undeclared (first use in this function)
29402 | patch_64(data + 0xb0, (uintptr_t)&_PyCriticalSection_BeginSlow);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
./jit_stencils.h:29405:39: error: ‘_PyCriticalSection_Resume’ undeclared (first use in this function)
29405 | patch_64(data + 0xc8, (uintptr_t)&_PyCriticalSection_Resume);
| ^~~~~~~~~~~~~~~~~~~~~~~~~
./jit_stencils.h: In function ‘emit__LOAD_FROM_DICT_OR_DEREF’:
./jit_stencils.h:31160:40: error: ‘_PyCriticalSection_BeginSlow’ undeclared (first use in this function)
31160 | patch_64(data + 0x1d8, (uintptr_t)&_PyCriticalSection_BeginSlow);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
./jit_stencils.h:31163:40: error: ‘_PyCriticalSection_Resume’ undeclared (first use in this function)
31163 | patch_64(data + 0x1f0, (uintptr_t)&_PyCriticalSection_Resume);
| ^~~~~~~~~~~~~~~~~~~~~~~~~
./jit_stencils.h: In function ‘emit__STORE_DEREF’:
./jit_stencils.h:38406:39: error: ‘_PyCriticalSection_BeginSlow’ undeclared (first use in this function)
./jit_stencils.h:38408:39: error: ‘_PyCriticalSection_Resume’ undeclared (first use in this function)
make: *** [Makefile:3018: Python/jit.o] Error 1
It looks like you’re trying to build with --disable-gil too, which is something we hadn’t really tested yet. That issue is being tracked here.
Does the build succeed on normal, non-free-threaded builds?
When I only pass --enable-experimental-jit=yes-off to the non-free-threaded builds, I get:
python3.13 /builddir/build/BUILD/Python-3.13.0b1/Tools/jit/build.py x86_64-redhat-linux-gnu --debug
==============================================================
JIT support for x86_64-redhat-linux-gnu is still experimental!
Please report any issues you encounter.
==============================================================
python3.13 /builddir/build/BUILD/Python-3.13.0b1/Tools/jit/build.py x86_64-redhat-linux-gnu --debug
==============================================================
JIT support for x86_64-redhat-linux-gnu is still experimental!
Please report any issues you encounter.
==============================================================
gcc -c -fno-strict-overflow -Wsign-compare -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -DDYNAMIC_ANNOTATIONS_ENABLED=1 -fcf-protection -fexceptions -fcf-protection -fexceptions -fcf-protection -fexceptions -O0 -Wno-cpp -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Wno-complain-wrong-lang -Werror=format-security -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -march=x86-64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -mtls-dialect=gnu2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -D_GNU_SOURCE -fPIC -fwrapv -D_Py_TIER2=3 -D_Py_JIT -flto -fuse-linker-plugin -ffat-lto-objects -flto-partition=none -g -std=c11 -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wstrict-prototypes -Werror=implicit-function-declaration -fvisibility=hidden -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Wno-complain-wrong-lang -Werror=format-security -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -march=x86-64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -mtls-dialect=gnu2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -D_GNU_SOURCE -fPIC -fwrapv -O0 -Wno-cpp -I/builddir/build/BUILD/Python-3.13.0b1/Include/internal -I/builddir/build/BUILD/Python-3.13.0b1/Include/internal/mimalloc -IObjects -IInclude -IPython -I. -I/builddir/build/BUILD/Python-3.13.0b1/Include -fPIC -DPy_BUILD_CORE -o Python/jit.o /builddir/build/BUILD/Python-3.13.0b1/Python/jit.c
/builddir/build/BUILD/Python-3.13.0b1/Python/jit.c: In function ‘emit__MATCH_MAPPING’:
/builddir/build/BUILD/Python-3.13.0b1/Python/jit.c:395:1: error: expected expression before ‘int’
395 | int
| ^~~
/builddir/build/BUILD/Python-3.13.0b1/Python/jit.c:479:1: error: expected declaration or statement at end of input
479 | }
| ^
In file included from /builddir/build/BUILD/Python-3.13.0b1/Python/jit.c:392:
./jit_stencils.h:30254:25: warning: unused variable ‘data_body’ [-Wunused-variable]
30254 | const unsigned char data_body[24] = {
| ^~~~~~~~~
./jit_stencils.h:30241:25: warning: unused variable ‘code_body’ [-Wunused-variable]
30241 | const unsigned char code_body[53] = {
| ^~~~~~~~~
/builddir/build/BUILD/Python-3.13.0b1/Python/jit.c: At top level:
/builddir/build/BUILD/Python-3.13.0b1/Python/jit.c:87:1: warning: ‘mark_executable’ defined but not used [-Wunused-function]
87 | mark_executable(unsigned char *memory, size_t size)
| ^~~~~~~~~~~~~~~
/builddir/build/BUILD/Python-3.13.0b1/Python/jit.c:70:1: warning: ‘jit_free’ defined but not used [-Wunused-function]
70 | jit_free(unsigned char *memory, size_t size)
| ^~~~~~~~
/builddir/build/BUILD/Python-3.13.0b1/Python/jit.c:49:1: warning: ‘jit_alloc’ defined but not used [-Wunused-function]
49 | jit_alloc(size_t size)
| ^~~~~~~~~
make: *** [Makefile:3018: Python/jit.o] Error 1
Hm, odd. Do you mind opening an issue, and tagging me? I’m also curious if there is some sort of invalid syntax in the generated jit_stencils.h file around the definition of emit__MATCH_MAPPING.
I’m also noticing now that the command-line to generate the JIT (and warning banner) are printed twice. That should only be running once per build, so I wonder if it’s a copy-paste error in your post, or if several builds are happening at once for some reason. If so, they could be stepping on each other and maybe truncating the generated file.
I can attach a full log for inspection when I open the issue.
Hello again. Possibly a silly question: What is the best way to verify the JIT is there? Calling a function several times and looking at the bytecode? Or is there some sys etc. attribute I can observe?
Great question. Honestly, the JIT may be a bit too transparent right now, so there aren’t really any great APIs for this yet.
If you want to know if a configure-based build enabled the JIT, you can use "_Py_JIT" in sysconfig.get_config_var("PY_CORE_CFLAGS").
If you want to know if a specific function contains jitted code, this is currently what we do in internal tests:
import _opcode
import types
def is_jitted(f: types.FunctionType) -> bool:
for i in range(0, len(f.__code__.co_code), 2):
try:
_opcode.get_executor(f.__code__, i)
except RuntimeError:
# This isn't a JIT build:
return False
except ValueError:
# No executor found:
continue
return True
return False
Here’s an example:
>>> def fibonacci(n):
... a, b = 0, 1
... for _ in range(n):
... a, b = b, a + b
... return a
...
>>> is_jitted(fibonacci)
False
>>> fibonacci(100)
354224848179261915075
>>> is_jitted(fibonacci)
True
This should help if you’re just curious, but if you’d like more robust/efficient APIs in sys or dis, then please open an issue letting us know what sort of functionality would be most useful to you!
Thanks. I just needed to verify if my changes to the Fedora package actually work as intended (JIT disabled by default, enabled with the environment variable).
I can use the example provided for a manual test.
A higher-level function/check might be easier for a smoke CI test, but it’s not required for Fedora.
In light of the discussion, we’re going to work on updating/revising the PEP.
While I start to compile the themes raised here, I also wanted to check in and see if there were any additional or new points that folks would like to see clarified that were not already mentioned.
24 posts were split to a new topic: JIT performance possibilities
This topic is temporarily closed for at least 4 hours due to a large number of community flags.
This topic was automatically opened after 12 hours.
Is it possible that this no longer works on Python 3.14?
The mechanism still works for detecting whether JIT code exists, yeah. However, in 3.14 we increased the JIT warmup threshold from 16 loops to 4096, so code will need to run a bit longer to be compiled:
>>> def fibonacci(n):
... a, b = 0, 1
... for _ in range(n):
... a, b = b, a + b
... return a
...
>>> is_jitted(fibonacci)
False
>>> fibonacci(4096)
4612001732280431247456445708563614127173224997617390534215059226137357133453956236072775985077061637311848907129417864574275423997101439882308358166652317363373656716074141072814493065517475413688262677419077617088948496309673353922704120725679705669386361748442871720790233981292904246541321855474289727005675146240418903692583131115962989146454578739972233255840007113102596686397958930124518885822059783685448190039658062872691964066428723178769322339485834664335313247796472730324095846596733944704930052412653763777113749102514483039561246866695780115646150369678333299122486379683222039167477498691611996122878629556831081616202064636498715093853352203252703786287926199052408354498825123496861419106453928530148716831934981264321286848387438601077819789292236505514653845305057927646386419899455438488952785050077521931600327064840520442470066917947
>>> is_jitted(fibonacci)
True
In general, this value shouldn’t be relied on. But realistically, 16 (the 3.13 behavior) is about the most aggressive value we’d ever use, and 4096 (the 3.14 behavior) is about the least aggressive value we’d ever use. Likely we’ll settle on something between them once the JIT is able to handle more edge-cases in ~3.15.
@brandtbucher Are you planning increase jit performance via inner functions specialization and condition removing? For example:
_Py_Dealloc(Py_DECREF) maybe transformed from dynamic function calling to calling specailized object function;cfunction_vectorcall_NOARGS→ call required function inline. Function_PyObject_MakeTpCallhave a lot of conditions which maybe cut by jit;PyFloat_FromDoublemay have two possible uops:-
PyObject * PyFloat_FromDouble(double fval) { PyFloatObject *op = _Py_FREELIST_POP(PyFloatObject, floats); if (op == NULL) { op = PyObject_Malloc(sizeof(PyFloatObject)); if (!op) { return PyErr_NoMemory(); } _PyObject_Init((PyObject*)op, &PyFloat_Type); } op->ob_fval = fval; return (PyObject *) op; } -
PyObject * PyFloat_FromDouble(double fval) { PyFloatObject *op = _Py_FREELIST_POP(PyFloatObject, floats); op->ob_fval = fval; return (PyObject *) op; }
-
long_add(_BINARY_OP_ADD_INT) - convert to uopsLONG_ADD_BOTH_COMPACT,LONG_ADD_X_SUB,LONG_ADD_X_ADD;
There are a lot of examples in source code. I found JIT Planning for 3.15 and 3.16 · Issue #139038 · python/cpython · GitHub, but no specifics about specialization there.
And the second question, are you planning tier3 jit with more aggressive optimizations and codegen at fly? Like ion in spidermonkey. Maybe this tier3 compiler will be llvm-based?
We already do some of what you said, and have plans for some of them.
_Py_Dealloc(Py_DECREF) maybe transformed from dynamic function calling to calling specailized object function;
This is already done in limited form. See Eliminate redundant refcounting in the JIT · Issue #134584 · python/cpython · GitHub. It’s currently blocked on reigster allocation.
cfunction_vectorcall_NOARGS
We haven’t done that yet, but that seems doable.
PyFloat_FromDoublemay have two possible uops
Or even better, just unbox them and avoid the freelist/allocation altogether.
I’m not sure what you mean by the long_add one.
And the second question, are you planning tier3 jit with more aggressive optimizations and codegen at fly? Like ion in spidermonkey. Maybe this tier3 compiler will be llvm-based?
This is still up in the air.