JIT build not working 3.13

Hi! I am trying out the new experimental jit feature. These are the following steps I have taken:

  1. clone the repository
  2. checkout branch 3.13
  3. run ./configure --enable-experimental-jit --prefix=/usr/local/python3.13j
  4. make altinstall
  5. Start the interpreter: /usr/local/python3.13j/bin/python3.13

Then, inside the intepreter I test to se if the jit is working or not

Python 3.13.0rc1 (TIMESTAMP) [Clang 15.0.0 (clang-1500.3.9.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sysconfig
>>> "_Py_JIT" in sysconfig.get_config_var("PY_CORE_CFLAGS")
False

In my understanding, from reading other posts on this forum, it should return true. See following: PEP 744: JIT Compilation - #53 by brandtbucher

Am I missing something in the configuration or building step?

Hi Andreas! Thanks for trying out the new JIT.

I followed all of your steps, and it seems to have built correctly for me. Do you mind sharing the value of sysconfig.get_config_var("PY_CORE_CFLAGS")? Here’s mine:

>>> sysconfig.get_config_var("PY_CORE_CFLAGS")
'-fno-strict-overflow -Wsign-compare -Wunreachable-code -DNDEBUG -g -O3 -Wall -D_Py_TIER2=1 -D_Py_JIT -std=c11 -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wstrict-prototypes -Werror=implicit-function-declaration -fvisibility=hidden  -I./Include/internal -I./Include/internal/mimalloc -I. -I./Include -DPy_BUILD_CORE'

In particular, it would help to see the logs from your configure and make steps:

  • The configure step should have the line: checking for --enable-experimental-jit... -D_Py_TIER2=1 -D_Py_JIT
  • Most of the compile commands in the make step should have those two flags: -D_Py_TIER2=1 -D_Py_JIT

There should also be a banner printed at some point in the make command that looks something like this:

=================================================================
JIT support for aarch64-apple-darwin23.6.0 is still experimental!
             Please report any issues you encounter.             
=================================================================

When I run it now, I get the following:

>>> import sysconfig
>>> sysconfig.get_config_var("PY_CORE_CFLAGS")
'-fno-strict-overflow -Wsign-compare -Wunreachable-code -DNDEBUG -g -O3 -Wall -D_Py_TIER2=1 -D_Py_JIT -std=c11 -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wstrict-prototypes -Werror=implicit-function-declaration -fvisibility=hidden  -I./Include/internal -I./Include/internal/mimalloc -I. -I./Include -DPy_BUILD_CORE'
>>> exit()

Which is interesting, I can clearly see that it is there. But, "_Py_JIT" in sysconfig.get_config_var("PY_CORE_CFLAGS") still returns false. Which I got most confused from. Thanks for clearing it up!

Result from the logs looks okay also:

cat config.log | grep ‘checking for --enable-experimental-jit’
configure:8289: checking for --enable-experimental-jit

So I think that proves that the JIT is active. Do you have an example to show that the JIT is working in runtime? I tried for example running the following program from the same thread:

def f(a, b):
    return a + b


f(1, 2)

a = 0

for i in range(100000000):
    a += f(1, 2)

print(a)

Using pypy for example, runs this very fast. But, my experimental jit does not improve the computation of this. Any suggestions of what might be the problem? Or am I simply not testing the jit correctly?

Thanks!

I believe the experimental jit currently just setting up infrastructure for future improvements. So you probably shouldn’t expect any significant performance changes right now.

1 Like

Maybe this shows some JIT effect, adjust number to see whether the effect increases or decreases:

import time

def workload_func():
    def inner():
        [y for y in [x for x in range(5000)]]
    [inner() for x in range(50)]

def run():
    def inner():
        [workload_func() for x in range(10)]
    return inner

def main():
    # use fast local variable
    local_func = run()

    # Warm up?
    [local_func() for x in range(100)]

    t0 = time.perf_counter()
    [local_func() for x in range(100)]
    dt = time.perf_counter() - t0
    print(f"Took {dt} seconds.")

if __name__ == '__main__':
    main()

I see! Makes sense since it is just introduced!

I gave it a try, but I did not seem to get an increase in performance unfortunaly!

Weird, for this code I consistently get a 15% speedup with PYTHON_JIT=1 over PYTHON_JIT=0, and an even bigger increase in relative performance for a --enable-experimental-jit --enable-optimizations --with-lto build.

Could you please add checks for whether workload_func() and run() are JITted using the code below?

def is_jitted(f: types.FunctionType) -> bool:
    for i in range(0, len(f.__code__.co_code), 2):
        try:
            print(f"Valid executor for {f.__name__}: {_opcode.get_executor(f.__code__, i).is_valid()}")
        except RuntimeError:
            # This isn't a JIT build:
            return False
        except ValueError:
            # No executor found:
            continue
        return True
    return False

Then in main(), after the warm up, add:

    print(f"{is_jitted(workload_func)=}")
    print(f"{is_jitted(run)=}")