JIT Compilation
debt(d5/e3/b3/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints list blackfire, opcache-gui, and phpbench — all specialist profiling/benchmarking tools. The common mistake of enabling JIT without benchmarking won't surface as a linter warning or compiler error; it requires deliberately running a profiler or benchmark suite to observe the lack of improvement (or added memory overhead). The silent-disabling case (missing jit_buffer_size) is particularly invisible without opcache-gui or similar inspection.
Closest to 'simple parameterised fix' (e3). The quick_fix describes benchmarking and toggling opcache.jit=tracing vs disabled — a configuration-level change in php.ini or runtime settings, not a code refactor. However, if JIT is found unhelpful and OPcache settings need tuning across environments (dev/staging/prod configs), it's slightly more than a one-liner, landing at e3 rather than e1.
Closest to 'localised tax' (b3). JIT configuration applies to web and CLI contexts (per applies_to), but it's an ini/config-level concern rather than permeating application code. It imposes a recurring burden of maintaining and validating the config (especially jit_buffer_size and workload benchmarks), but most of the codebase is unaffected. The confusion with OPcache adds a small ongoing cognitive tax for maintainers.
Closest to 'serious trap' (t7). The misconception field states explicitly that enabling JIT is assumed to always improve performance — but for typical CRUD/I/O-bound PHP web apps (the dominant PHP use case), it provides no benefit and adds memory overhead. This directly contradicts the reasonable expectation that a 'performance feature' improves performance universally, and contradicts how JIT is often marketed. The silent disabling (no jit_buffer_size) compounds the trap. This is a well-documented gotcha that contradicts developer intuition, placing it at t7.
Also Known As
TL;DR
Explanation
JIT compilers profile running code to identify hot paths (frequently executed), then compile those paths to native machine code. Two strategies: method JIT (compile entire methods) and tracing JIT (compile frequently executed traces across method boundaries). PHP 8's JIT uses a tracing approach. JIT benefits CPU-bound code (math, algorithms, image processing); I/O-bound PHP web apps see minimal improvement because they spend most time waiting for database/network, not executing CPU instructions.
Diagram
flowchart TD
SRC[PHP Source] --> LEX[Lexer]
LEX --> AST2[AST]
AST2 --> OPC[Opcodes]
OPC --> JIT{JIT enabled?}
JIT -->|no| INTERP[Interpreter<br/>execute opcode by opcode]
JIT -->|yes| PROFILE[Profile hot paths]
PROFILE --> COMPILE[Compile to native x86<br/>machine code]
COMPILE --> NATIVE[Execute native code<br/>much faster for CPU-bound]
NATIVE -.->|cache in opcache| COMPILE
INFO[JIT helps: math, compression, image processing<br/>JIT does NOT help: DB or IO bound apps]
style NATIVE fill:#238636,color:#fff
style INTERP fill:#d29922,color:#fff
style INFO fill:#1f6feb,color:#fff
Common Misconception
Why It Matters
Common Mistakes
- Enabling JIT without benchmarking — JIT adds memory overhead; verify it actually improves your specific workload.
- Expecting JIT to fix slow database queries — JIT only affects CPU execution time, not network and disk I/O.
- Not setting opcache.jit_buffer_size — JIT silently disabled without a buffer allocation.
- Conflating OPcache (always beneficial) with JIT (workload dependent).
Code Examples
; php.ini — JIT enabled for typical web CRUD app:
opcache.jit=1255
opcache.jit_buffer_size=256M
; Web app spends 95% of time in MySQL queries
; JIT improves the 5% PHP execution — negligible real-world improvement
; 256MB memory wasted on unused JIT buffer
; php.ini — JIT for CPU-bound workload (image processing, ML inference):
opcache.jit=1255 ; Tracing JIT
opcache.jit_buffer_size=128M
; Benchmark first: wrk -t4 -c100 -d30s http://localhost/
; Compare: with/without JIT on your specific workload
; For typical CRUD: keep jit_buffer_size small or disable JIT entirely