A foreword of warning about the JIT of PyPy as of March 2007: single functions doing integer arithmetic get great speed-ups; about anything else will be a bit slower with the JIT than without. We are working on this - you can even expect quick progress, because it is mostly a matter of adding a few careful hints in the source code of the Python interpreter of PyPy.
By construction, the JIT is supposed to work correctly on absolutely any kind of Python code: generators, nested scopes, exec statements, sys._getframe().f_back.f_back.f_locals, etc. (the latter is an example of expression that Psyco cannot emulate correctly). However, there are a couple of known issues for now (see Caveats).
So far there is little point in trying the JIT on anything else than integer arithmetic-intensive functions (unless you want to help find bugs). For small examples, you can also look at the machine code it produces, but if you do please keep in mind that the assembler will look fundamentally different after we extend the range of PyPy that the JIT generator processes.
Go to pypy/translator/goal/ and run:
./translate.py --jit targetpypystandalone
Please read the Status section above first.
This will produce the C code for a version pypy-c that includes both a regular interpreter and an automatically generated JIT compiler. This pypy-c uses its interpreter by default, and due to some overhead we expect this interpreter to be a bit slower than the one found in a pypy-c compiled without JIT.
In addition to --jit, you can also pass the normal options to translate.py to compile different flavors of PyPy with a JIT. See the compatibility matrix for the combinations known to be working right now. (The combination of the JIT with the thunk or taint object spaces probably works too, but we don't expect it to generate good code before we drop a few extra hints in the source code of the object spaces.)
You can mark one or many code objects as candidates for being run by the JIT as follows:
>>>> def f(x): return x*5 >>>> import pypyjit >>>> pypyjit.enable(f.func_code) >>>> f(7) # the JIT runs here 35 >>>> f(8) # machine code already generated, no more jitting occurs here 40
A few examples of this kind can be found in demo/jit/. The script demo/jit/f1.py shows a function that becomes seriously faster with the JIT - only 10% to 20% slower than what gcc -O0 produces from the obvious equivalent C code, a result similar to Psyco. Although the JIT generation process is well-tested, we only have a few tests directly for the final pypy-c. Try:
pypy-c test_all.py module/pypyjit/test/test_pypy_c.py -A --nomagic
You can get a dump of the generated machine code by setting the environment variable PYPYJITLOG to a file name before you start pypy-c. See In more details above. To inspect this file, use the following tool:
python pypy/jit/codegen/i386/viewcode.py dumpfilename
The viewcode.py script is based on the Linux tool objdump to produce a disassembly. It should be easy to port to OS/X. If you want to port the tool to Windows, have a look at http://codespeak.net/svn/psyco/dist/py-utils/xam.py : this is the tool from which viewcode.py was derived in the first place, but Windows-specific parts were omitted for lack of a Windows machine to try them on.
When running JIT'ed code, the bytecode tracing hook is not invoked. This should be the only visible effect of the JIT, aside from the debug prints and the speed/memory impact. In practice, of course, it still has also got rough edges.
One of them is that all compile-time errors are fatal for now, because it is hard to recover from them. Clearly, the compiler is not supposed to fail, but it can occur because the memory runs out, a bug hits, or for more subtle reasons. For example, overflowing the stack is likely to cause the JIT compiler to try to compile the app-level handlers for the RuntimeError, and compiling takes up stack space too - so the compiler, running on top of the already-full stack, might hit the stack limit again before it has got a chance to generate code for the app-level handlers.