Skip to main content
← All articles
php

Why turning on JIT won't speed up your Drupal site (and the math that proves it)

Why turning on JIT won't speed up your Drupal site (and the math that proves it)

Every so often someone drops opcache.jit=tracing into a production php.ini, restarts PHP-FPM, and waits for the Drupal site to get faster. It doesn't. Not meaningfully, anyway. This isn't a tuning failure — it's arithmetic, and the arithmetic was spelled out years ago by the people who built the JIT in the first place.

The JIT's own authors told you this

PHP got a JIT (just-in-time compiler) in PHP 8.0. It turns hot PHP opcodes into native machine code instead of interpreting them. Sounds like free speed. But the JIT RFC itself says, in plain text: it "currently doesn't seem to significantly improve real-life apps like WordPress" — and quotes the numbers, 326 req/sec with JIT versus 315 without. That's about 3.5%. The people proposing the feature led with the caveat that it barely moves a CMS.

Brent Roose's follow-up benchmark on a real Laravel app found the same shape: single-digit percent at best, and within noise on a second run. His conclusion: JIT "probably won't have a significant impact on web applications." Meanwhile his CPU-bound fractal benchmark got roughly a 10× speedup. That gap is the entire story.

The math that proves it (Amdahl's law)

A typical Drupal request doesn't spend its time running PHP loops. It spends it waiting — on database queries, on the filesystem, on network. Say a request takes 100ms: 90ms of I/O wait and 10ms of actual CPU-bound PHP. The JIT can only touch that 10ms.

Be generous and say JIT makes the CPU slice 1.2× faster: 10ms becomes about 8.3ms. New total: 98.3ms. You just made your site 1.7% faster. Now be absurdly generous — make the CPU slice infinitely fast, 10ms to 0: total is 90ms. That's an 11% ceiling you can never exceed, no matter how good the JIT gets, because 90% of the request was never CPU in the first place. To get a 2× speedup from JIT, the interpreted CPU work would have to dominate the request — which is precisely what a database-backed CMS page is not.

JIT makes CPU-bound code faster. A Drupal page isn't CPU-bound; it's a coordinator of I/O. You can't optimise your way past a bottleneck you're not in.

What actually speeds up the site

Spend the effort where the time actually goes. In rough order of payoff on a real Drupal box:

  1. OPcache, tuned. This is the real win from that same extension. Give it enough memory (opcache.memory_consumption=192 or more for a big site), raise opcache.max_accelerated_files well past the default 10,000 (Drupal plus contrib blows through that — count with find . -name '*.php' | wc -l), and in production set opcache.validate_timestamps=0 so PHP stops stat-ing every file on every request. The catch: with timestamps off you must reset OPcache on each deploy, or your new code never runs.
  2. OPcache preloading (PHP 7.4+) to keep core classes resident across requests.
  3. A real cache backend. Move Drupal's cache bins off the database into Redis or Memcached. This directly attacks the 90% — fewer, faster round-trips.
  4. The render/page/dynamic-page cache, BigPipe, and a reverse proxy or CDN in front. Again: the I/O and the rendering, not the interpreter.

Where JIT genuinely earns its keep

I want to be fair, because "JIT is useless" is as wrong as "turn it on for free speed." JIT is a real win for CPU-bound PHP: image manipulation, numeric and scientific work, cryptography, long-running CLI jobs, anything that's a tight loop of computation rather than a wait for a query. And in a persistent-worker setup — FrankenPHP worker mode, where the app boots once and stays in memory — the time profile shifts toward execution, so JIT plus preloading has more surface to optimise. If that's your workload, benchmark it and enjoy the gains.

(One version footnote so this ages well: PHP 8.4 changed the JIT defaults — opcache.jit now defaults to disable while the buffer size defaults to 64M. So "turn on JIT" means different things on different versions; check before you cargo-cult a config line from a 2021 blog.)

My take

This blog is boring, I/O-bound Drupal on a modest box. JIT is off, and turning it on would buy me a rounding error while adding memory pressure I'd rather spend on OPcache and the InnoDB buffer pool. The honest move isn't to sprinkle a magic php.ini line and hope — it's to open a profiler, find where the 90% actually goes, and realise it was never in the part JIT can touch.

Links

BM
Blue Moose
The moose behind Blue Moose. Full-stack PHP developer — Drupal by day, Symfony by night, tests always.