In PHP 8.5, how do you turn on the JIT compiler, and which workloads actually get faster with it?
answer
- part of OPcache, off by default
- opcache.jit=disable is the 8.4+ default
- tracing (1254) versus function (1205)
- opcache.jit_buffer_size defaults to 64M
- CPU-bound loops, not I/O waits
basics
~20 sSet opcache.jit=tracing (the recommended mode) with OPcache enabled; since PHP 8.4 the default is opcache.jit=disable with a 64M opcache.jit_buffer_size. The JIT speeds up CPU-bound PHP such as numeric loops and parsers; typical web requests waiting on databases gain little.
solid answer
~40 sThe JIT lives inside OPcache and compiles hot opcodes to machine code. Since PHP 8.4 it is off through `opcache.jit=disable`, while `opcache.jit_buffer_size` defaults to `64M`; before 8.4 the mode defaulted to `tracing` and the buffer to `0`, so old guides enable it by setting only the buffer — which no longer works. I set `opcache.jit=tracing` (same as `on`, the four-digit CRTO code `1254`), or `function` (`1205`) to compile whole functions. `disable` cannot be switched on at runtime; `off` can. For CLI jobs I also need `opcache.enable_cli=1`. What gets faster is **CPU-bound PHP**: tight loops, maths, image or text processing, long-running workers. A typical web request spends most of its time waiting on the database and network, so the JIT changes little there. I verify with `opcache_get_status()['jit']` and a benchmark rather than assuming.
code
ini · 5 lines; enable the tracing JIT (PHP 8.4+ defaults: jit=disable, buffer 64M)
opcache.enable=1
opcache.enable_cli=1 ; only if CLI jobs should use it
opcache.jit=tracing
opcache.jit_buffer_size=128Mgo deeper
Know that PHP has a JIT inside OPcache, that it is off by default, and that it speeds up CPU-heavy code rather than I/O.
Explain the modes — disable, off, tracing, function — and the PHP 8.4 change of defaults that makes the buffer size alone insufficient.
Decide per workload with benchmarks: enable tracing for CPU-bound workers, keep web fleets on measured evidence, and watch buffer use and extension conflicts.
Frame the JIT as one lever in a performance budget, prioritised after I/O and algorithmic work, with its memory and risk costs made explicit.
## What the JIT is PHP normally runs **opcodes** in the Zend Engine's interpreter: each opcode is dispatched to a handler written in C. The **JIT** (just-in-time compiler), added in PHP 8.0 as part of OPcache, compiles frequently executed opcodes into native machine code, removing dispatch overhead and using type information to specialise operations. Because it is part of OPcache, it needs OPcache active in that process: `opcache.enable=1` for web SAPIs, and `opcache.enable_cli=1` for command-line scripts. ## The switches | Directive | Default (PHP 8.4+) | Meaning | |---|---|---| | `opcache.jit` | `disable` | mode: `disable`, `off`, `tracing`/`on`, `function`, or a four-digit CRTO number | | `opcache.jit_buffer_size` | `64M` | shared memory reserved for machine code; `0` disables the JIT | | `opcache.jit_hot_loop` | `61` | loop iterations before a loop counts as hot (PHP 8.5 made it a prime) | | `opcache.jit_hot_func` | `127` | calls before a function counts as hot | The mode values: - **`disable`**: completely disabled; it cannot be enabled at runtime with `ini_set()`. - **`off`**: JIT machinery present but not compiling; can be switched on at runtime. - **`tracing`** (also `on`): profiles on the fly and compiles hot code paths — traces through loops and calls. Equivalent to CRTO `1254`. The manual recommends it for most users. - **`function`**: compiles whole functions. Equivalent to CRTO `1205`. The four digits of **CRTO** are CPU-specific flags, register allocation, trigger and optimisation level; the named modes cover almost every real need. ## The PHP 8.4 change that trips people up Before PHP 8.4 the defaults were `opcache.jit=tracing` and `opcache.jit_buffer_size=0`: the mode was set but the zero buffer kept the JIT off, and guides said "set `jit_buffer_size` to enable the JIT". Since 8.4 the defaults are `opcache.jit=disable` and `opcache.jit_buffer_size=64M`. Setting only the buffer size now does nothing; you must set `opcache.jit`. Also since 8.4, if the JIT is enabled and fails to initialise, PHP exits with a fatal error at startup instead of silently running without it. ## Which workloads benefit The JIT speeds up **time spent executing PHP opcodes**. It does nothing for time spent waiting. | Workload | Expected effect | |---|---| | numeric loops, simulations, hashing in PHP code | large gains | | parsing, templating engines written in PHP, image or text processing | moderate gains | | long-running CLI workers doing computation | moderate gains | | typical CRUD web request: routing, database, HTTP calls | little change | A request that spends most of its time waiting on a database is still dominated by that wait. ## Costs and compatibility 1. **Memory**: the buffer is reserved in shared memory on top of `opcache.memory_consumption`. 2. **Extensions that hook execution**: when an extension overrides the engine's execute function or installs its own opcode handlers, as some debuggers and profilers do, the JIT disables itself with a warning. 3. **Correctness risk**: the JIT is mature but more complex than the interpreter; `opcache_jit_blacklist()`, added in PHP 8.4, excludes a specific closure from compilation when needed. ## Verifying and deciding - `opcache_get_status()['jit']` shows whether it is `on` and how much of the buffer is used. - Benchmark the real workload with the JIT `disable`d and with `tracing`; keep it only if the gain is measurable. - For web fleets, the JIT is an option to test, not a default fix; for CPU-heavy batch or worker processes it is often the easiest speed-up available. ## A short decision checklist 1. **Profile first.** If most wall time is I/O, fix queries, caching and network calls; the JIT cannot help there. 2. **Isolate the CPU-heavy part.** Report generation, image work or data crunching often runs in CLI jobs or workers; enable the JIT for those processes only, with `opcache.enable_cli=1` where needed. 3. **Start with `tracing`.** It is the recommended mode; try `function` only if tracing shows no gain. 4. **Watch the buffer.** If `opcache_get_status()['jit']` shows the buffer full, compiled code stops growing; raise `opcache.jit_buffer_size`. 5. **Check extensions.** A warning that the JIT was disabled at startup usually means an extension that hooks execution is loaded, which usually belongs to a development setup rather than production.
- What is the difference between opcache.jit=disable and opcache.jit=off?`disable` switches the JIT off completely at startup, and `ini_set('opcache.jit', 'tracing')` later fails with a warning. `off` keeps the JIT initialised but not compiling, so code can switch it on at runtime, for example only in a CPU-heavy command.
- Why might enabling the JIT show no improvement on a web application?Most of a typical request's time is spent outside PHP opcodes: waiting for the database, a cache or an HTTP API. The JIT only speeds up opcode execution, so if that is a small share of the request, the overall gain is small.
saying these in an interview costs you the question
- Setting opcache.jit_buffer_size alone enables the JIT in PHP 8.5
- The JIT makes database-bound web requests much faster
- The JIT works without OPcache
- opcache.jit=disable can be switched on later with ini_set()
- The JIT is enabled by default since PHP 8.0