PHP 8.0: JIT
PHP 8.0 brings support for Just-In-Time Compilation (JIT).
Pure interpreted programming languages has no compilation step, and directly executes the code in a virtual machine. Most of the interpreted languages including PHP, in fact, has a light-weight compilation step to improve its performance.
Programming languages with Ahead-Of-Time (AOT) compilation, on other hand requires the code to be compiled first before it runs.
Just-In-Time compilation is a hybrid model of interpreter and Ahead-of-Time compilation, that some or all of the code is compiled, often at run-time, without requiring the developer to manually compile it.
PHP was historically an interpreted language, that all of the code was interpreted by a virtual machine (Zend VM). This was changed with the introduction of Opcache and Opcodes, which were generated from the PHP code, and can be cached in memory. PHP 7.0 added the concept of AST (Abstract Syntax Tree), that further separated the parser from the compiler.
PHP's JIT internally uses DynASM from LuaJIT, and as implemented as part of the Opcache.
Opcache can inspect the used code, commonly called hot code, and store the compiled versions of them within the shared Opcache memory. When the code should be compiled, and what code should be compiled is configurable.
JIT is currently enabled on Linux and Windows systems running on x86 and x64 processor instruction sets. Apple M1, and ARM CPUs are currently not supported.
DynASM, which is the underlying assembler used in PHP JIT, supports ARM instructions as well, but it is not clear if PHP JIT can be run on ARM processors yet.
JIT also makes use of AVX if it is supported on the CPU. Most consumer and server grade processors from 2011 and later support AVX instructions.
cat /proc/cpuinfo | grep avx run on most POSIX systems should reveal if the processor supports it.
In PHP 8.0, JIT is enabled by default, but turned off. Enabling JIT is easy, and only requires minimal INI configuration similar to this:
opcache.enable=1 opcache.enable_cli=1 opcache.jit_buffer_size=256M
JIT is implemented as part of Opcache, and requires the Opcache extension to be enabled.
opcache.enable=1directive does not enable Opcache for CLI, and requires
opcache.enable_cli=1. It is possible to turn off Opcache (and this JIT) for other SAPIs (such as FPM), and enable Opcache only for CLI with
The effective toggle for JIT is
opcache.jit_buffer_size. It accepts how much memory JIT is allowed to use for its buffer.
The default value
0, which effectively disables JIT.
To enable and set a buffer size, set a positive value in bytes, or with standard PHP data size suffixes (
Additional JIT Configuration Options
The following configuration options are available, but they are set to appropriate defaults.
opcode.jit is a somewhat complicated configuration value. It accepts
function, and a 4-digit value (not a bitmask) of 4 different flags in the order.
disable: Completely disables JIT feature at start-up time, and cannot be enabled run-time.
off: Disabled, but it's possible to enable JIT at run-time.
tracing: An alias to the granular configuration
function: An alias to the granular configuration
In addition to the
function aliases, the
opcache.jit directive accepts a 4-digit configuration value as well. it can further configure the JIT behavior.
The 4-digit configuration value is in the form of
CRTO, where each position allows a single digit value for the flag designated by the letter.
For the full configuration options, see JIT Flags.
The default value is
tracing, which compiles hot code (call-graph) while the code is being run (tracing), and enables use of CPU registers and AVX extension.
PHP JIT provides a way to emit JIT debug information by setting an INI configuration. When set, it outputs the assembly code for further inspection.
opcache.jit_debug directive accepts a bit-mask value to toggle certain features. It currently accepts a value in the range of 1 (
0b1) to 20 bits binary. A value of
1048576 represents the maximum level of debug output.
php -d opcache.jit_debug=1 test.php
TRACE-1$/test.php$7: ; (unknown) sub $0x10, %rsp mov $EG(jit_trace_num), %rax mov $0x1, (%rax) .L1: cmp $0x4, 0x58(%r14) jnz jit$$trace_exit_0 cmp $0x4e20, 0x50(%r14) ...
More Configuration Options
JIT supports several other configuration options to tune how many function calls makes it a "hot" function, which JIT then compiles, and a threshold to consider which functions to JIT, based on the percentage of overall function calls.
For a full list, see Opcache Configuration.
opcache.jit directive accepts a 4-digit value to control the JIT behavior, in the form of
CRTO, and accepts following values for
C: CPU-specific Optimization Flags
0: Disable CPU-specific optimization.
1: Enable use of AVX, if the CPU supports it.
R: Register Allocation
0: Don't perform register allocation.
1: Perform block-local register allocation.
2: Perform global register allocation.
0: Compile all functions on script load.
1: Compile all functions on first execution.
2: Profile first request and compile the hottest functions afterwards.
3: Profile on the fly and compile hot functions.
4: Currently unused.
5: Use tracing JIT. Profile on the fly and compile traces for hot code segments.
O: Optimization Level
0: No JIT.
1: Minimal JIT (call standard VM handlers).
2: Inline VM handlers.
3: Use type inference.
4: Use call graph.
5: Optimize whole script.
4under Triggers (
T=4) did not make it to the final version of JIT implementation. It was trigger JIT on functions declared with
@jitDocBlock comment attribute. This is now unused.
Backwards Compatibility Impact
None. JIT is a new feature added in PHP 8.0, and should not cause any issues. PHP does not raise any warnings or errors when it encounters unknown INI directives, which means setting JIT INI directives would not cause any issues.