PHP 8's JIT Compiler: How It Removes the Interpreter Tax
PHP 8's JIT compiles opcodes into native machine code, removing interpreter overhead. This piece covers OPcache, Tracing vs Function JIT, and CRTO tuning.
PHP's traditional execution model compiles source code into opcodes and interprets them through the Zend VM loop. Because every step involves dynamic type checks and zval management, this process carries a long-standing performance cost often described as the 'interpreter tax'.
Introduced in PHP 8.0 in November 2020, the JIT compiler builds on the existing OPcache infrastructure to translate opcodes directly into native machine code that the CPU executes without interpretation. Through type inference, when the compiler can prove a variable is always of a given type, it can emit a single machine instruction instead of the full generic zval-handling logic — a change that yields significant gains in tight numeric loops.
PHP offers two JIT strategies: the Function JIT, which compiles entire functions upfront regardless of which branches actually run, and the Tracing JIT, which profiles code at runtime, records hot execution paths, and specializes them based on observed types. The Tracing JIT is the default and recommended mode because it uses less memory and avoids wastefully compiling cold code.
JIT behavior is configured through the opcache.jit directive using a four-digit CRTO format, and the JIT remains inactive unless opcache.jit_buffer_size is set above zero. These configuration details matter directly to engineers seeking to understand and tune PHP application runtime performance.