JIT On vs JIT Off: PHP 8.5 JIT Performance Under I/O Latency (2026)

In our previous benchmarks, we kept JIT disabled to isolate the impact of runtime architecture on performance. Readers immediately asked: “What happens when you turn JIT on?” Today, we answer that question definitively—by re-running our comprehensive four-environment, three-tier I/O latency test with PHP 8.5’s JIT compiler fully enabled.

The results are surprising. JIT not only failed to improve performance in I/O-bound workloads—it actually made most environments slightly slower. This finding challenges the common assumption that JIT is a universal performance booster and reveals a fundamental truth: JIT accelerates CPU-bound code, not I/O-bound applications.


Understanding the JIT Paradox

Before diving into the numbers, it is essential to understand why JIT behaves differently under I/O latency.

In a CPU-bound scenario—such as our earlier benchmarks with pure computation loops—JIT compiles hot PHP code paths into native machine code, eliminating the interpretation overhead. This is why we observed massive gains in our JIT configuration impact analysis.

In an I/O-bound scenario, however, the bottleneck shifts from CPU to waiting. When your application spends 50ms, 200ms, or 500ms waiting for a database response, the CPU is idle regardless of how fast the PHP code executes. JIT’s compilation overhead—the CPU cycles spent analyzing and compiling hot code—becomes a net negative because the optimized code never gets a chance to shine.

This is the JIT paradox: the very mechanism that makes JIT powerful in CPU-intensive workloads becomes a liability when I/O dominates.


Test Environment & Methodology

All tests ran on a single Alibaba Cloud ECS instance to eliminate network variance. The software stack mirrors a typical production environment managed by the Baota panel.

For context on our testing methodology, see our previous benchmarks in this series.

Server Specifications

  • OS: Ubuntu 24.04 LTS
  • Web Server: Nginx 1.26 (for PHP‑FPM only)
  • PHP Versions: 8.5.2 (FPM, RoadRunner, Swoole) / 8.5.6 (FrankenPHP built‑in)
  • Database: MySQL 8.4
  • Control Panel: Baota (for convenient PHP‑FPM management)

JIT Configuration

For this test, JIT was enabled with the following production-grade settings:

DirectiveValue
opcache.jittracing
opcache.jit_buffer_size128M

These settings were verified across FPM, RoadRunner, and Swoole via php -i before testing began. FrankenPHP’s built-in PHP 8.5.6 does not support JIT configuration through scanned .ini files in this version and serves as a JIT-disabled reference point.

Four Environments, Three Latency Levels

The same Laravel 11 application from our previous tests was used, with routes performing 1,000 sqrt() calculations followed by a database query with controlled delay:

RouteSQLI/O Delay
/bench-slowSELECT SLEEP(0.05) as ok50ms
/bench-xslowSELECT SLEEP(0.2) as ok200ms
/bench-ultraSELECT SLEEP(0.5) as ok500ms

Load Testing Tool & Parameters

  • Tool: ApacheBench (ab) 2.3
  • Concurrency: 100 (-c 100) for 50ms and 200ms; 50 (-c 50) for RoadRunner 500ms
  • Duration: 60 seconds per run (-t 60)
  • Strategy: Each environment ran 3 consecutive rounds with a 10-second warm‑up. The first round was discarded; the final result is the average of rounds 2 and 3.

Benchmark Results

Bar chart comparing JIT On vs JIT Off RPS for PHP-FPM, RoadRunner, FrankenPHP, and Swoole across I/O latency levels

50ms Database Latency

EnvironmentJIT Off RPSJIT On RPSChange
PHP‑FPM309.3303.7-1.8%
RoadRunner392.3388.1-1.1%
FrankenPHP139.7129.7-7.2%
Swoole372.7363.7-2.4%

200ms Database Latency

EnvironmentJIT Off RPSJIT On RPSChange
PHP‑FPM96.294.3-2.0%
RoadRunner98.998.7-0.2%
FrankenPHP38.337.7-1.6%
Swoole98.197.6-0.5%

500ms Database Latency

EnvironmentJIT Off RPSJIT On RPSChange
PHP‑FPM38.938.7-0.5%
RoadRunner39.339.30.0%
FrankenPHP15.315.30.0%
Swoole39.239.20.0%

Key Takeaways

JIT provides zero benefit in I/O-bound workloads. Across all four environments and three latency levels, JIT either made performance slightly worse or had no measurable effect. The changes are all within -2.4% to 0.0%, confirming that JIT’s compilation overhead provides no advantage when the CPU is waiting on I/O.

The performance penalty decreases as I/O latency increases. At 50ms, the JIT overhead was most noticeable (up to -7.2% for FrankenPHP). At 200ms, the penalty shrank. At 500ms, the difference was negligible—because at that point, the I/O wait time so completely dominates the request lifecycle that any CPU-side optimization becomes irrelevant.

FrankenPHP is most affected by JIT overhead. Across all latency levels, FrankenPHP showed the largest relative performance decline when JIT was enabled. This suggests that FrankenPHP’s dynamic thread scheduling is particularly sensitive to the additional CPU cycles consumed by JIT compilation.

The ranking of runtimes does not change. RoadRunner remains the leader at every latency level, Swoole maintains second place, and FrankenPHP stays at the bottom. JIT does not alter the fundamental architectural advantages of each runtime.

RoadRunner shows remarkable stability. With corrected SLEEP-enabled benchmarks, RoadRunner’s performance at 500ms I/O latency (39.3 RPS) is identical to its JIT Off baseline—demonstrating that its Go-based goroutine scheduler efficiently handles blocking I/O regardless of JIT status.


Deep Analysis: Why JIT Fails Under I/O Load

The CPU-I/O Tradeoff

The fundamental issue is a mismatch between what JIT optimizes and what I/O-bound applications need.

JIT optimizes CPU time. It analyzes hot code paths, compiles them to native machine code, and eliminates the overhead of PHP’s interpreter loop. In a pure computation benchmark, this can yield 60-95% performance improvements, as we demonstrated in our earlier JIT tests.

I/O-bound applications need more workers, not faster code. When a request spends 500ms waiting for the database, the CPU is idle for 499 of those milliseconds. Saving 1ms of PHP execution time—even a 50% reduction—is utterly meaningless when the total request time is 501ms. The bottleneck is the database, not the PHP runtime.

The JIT Overhead Problem

JIT compilation is not free. It consumes CPU cycles for:

  1. Profiling: Identifying which code paths are “hot” enough to justify compilation.
  2. Compilation: Translating PHP opcodes into native machine code.
  3. Memory allocation: Reserving space in the JIT buffer for compiled code.

In a CPU-bound workload, these costs are quickly amortized by the performance gains of compiled code. In an I/O-bound workload, the compiled code never gets a chance to demonstrate its speed because the CPU is waiting on I/O for the vast majority of the request lifecycle.

The Laravel Framework Factor

Laravel itself adds a significant amount of overhead to each request—routing, middleware, service container resolution, and Eloquent ORM initialization. Much of this code is executed on every request, making it a prime candidate for JIT compilation.

However, the I/O latency in our test routes—50ms, 200ms, and 500ms—is orders of magnitude larger than any potential savings from JIT-compiled framework code. Even if JIT eliminated 10ms of framework overhead (an optimistic estimate), it would be invisible in a 500ms request.


The Complete JIT Decision Matrix

Combined with our earlier JIT tests across different workloads, we can now provide a comprehensive guide to when JIT is worth enabling.

Workload TypeJIT BenefitRecommendationReference
Pure CPU computation+61%EnableJIT On vs Off Benchmark
Laravel/Symfony CPU-heavy+80-95%EnableLaravel JIT Benchmark
WordPress CMS<5%OptionalWordPress JIT Benchmark
Light I/O (sub-millisecond DB)Varies by runtimeTest firstApplication Server Benchmark
Heavy I/O (50ms+ DB latency)0% to -8%DisableThis article
Microservices with slow APIs~0%DisableThis article
WebSocket servers~0%DisableThis article

Recommendations for Production

Based on the complete picture now available from our comprehensive JIT testing series, here is our guidance:

Enable JIT when:

  • Your application performs significant CPU computation (data processing, image manipulation, cryptographic operations).
  • You use Laravel or Symfony with complex routing and middleware stacks.
  • Your database queries consistently return in under 1ms.

Disable JIT when:

  • Your application is I/O-bound (slow database queries, external API calls, file operations).
  • You run WordPress or other CMS platforms with minimal custom computation.
  • You use WebSocket servers or real-time applications where latency predictability matters more than raw throughput.
  • You are memory-constrained (JIT buffer requires 128MB+ of dedicated RAM).

The safest approach: Benchmark your own application with JIT on and off using realistic traffic patterns. The results may surprise you—as they surprised us.


Full Reproducibility

At PHPBenchLab, we believe benchmarks should be transparent and repeatable. All test scripts, configuration files, and raw data are publicly available.

Test Endpoints (Publicly Accessible)

  • PHP‑FPM: https://phpbenchlab.com/bench-slow (and bench-xslow, bench-ultra)
  • RoadRunner: https://phpbenchlab.com:8081/bench
  • FrankenPHP: https://phpbenchlab.com:8082/bench-slow
  • Swoole: https://phpbenchlab.com:8083/bench-slow

JIT Configuration

opcache.jit = tracing
opcache.jit_buffer_size = 128M

Benchmark Command (after warm‑up)

ab -c 100 -t 60 http://phpbenchlab.com/bench-slow

Frequently Asked Questions

1. Why did JIT make performance worse instead of better?

JIT compilation consumes CPU cycles to profile and compile hot code. In I/O-bound workloads, the compiled code never gets a chance to demonstrate its speed because the CPU spends most of its time waiting for database responses. The compilation overhead becomes a net negative.

2. Should I disable JIT for my production application?

It depends on your workload. If your application is CPU-bound (heavy computation, complex framework routing), JIT can provide substantial benefits. If your application is I/O-bound (slow databases, external APIs), JIT provides no benefit and may slightly degrade performance. Benchmark with your own traffic patterns to decide.

3. Is this result specific to the tracing JIT mode?

We tested with opcache.jit = tracing, which is the recommended production setting. The function JIT mode has lower overhead but also lower optimization potential. In I/O-bound workloads, both modes would likely show similar results—negligible to zero benefit.

4. Does this mean JIT is useless?

Absolutely not. JIT provides massive performance improvements in CPU-bound workloads, as we demonstrated in our earlier benchmarks (+61% to +95%). The key insight is that JIT is a CPU optimizer, not an I/O accelerator. Understanding this distinction is crucial for making informed decisions about your PHP infrastructure.

5. Which application server benefits most from JIT?

In our testing, none of the four runtimes showed a meaningful benefit from JIT under I/O latency. However, in CPU-bound scenarios, RoadRunner and Swoole generally benefit more than FPM because their memory-resident architectures give JIT more opportunities to cache and reuse compiled code across requests.


What’s Next?

This test completes our comprehensive analysis of PHP 8.5’s JIT compiler across a wide range of workloads. In future articles, we plan to explore:

  • JIT with HTTP/2 load testing: Does JIT behave differently under multiplexed connections?
  • JIT with CPU-heavy database queries: Complex JOINs and aggregations that stress both CPU and I/O.
  • FrankenPHP deep tuning: Fixed worker counts and HTTP/2 impact on its performance.

Have a specific scenario you would like us to benchmark? Reach out via our contact page.

Found this article useful? Share it with your team or bookmark PHPBenchLab.com for more data‑driven PHP performance insights.

This JIT analysis forms Pillar #2 of our larger PHP performance optimization guide, which provides a complete decision tree for all major PHP 8.5 performance choices.

Leave a Comment