📁 Volume II: Laravel Performance Tuning Kit

⚡ Topic 17: OpCache & PHP JIT

PHP's built-in performance superpowers.

"Without OpCache, your PHP code is compiled on EVERY request.
With OpCache, it's compiled once and stored in memory.
The difference is 10-30ms per request. It's free performance.
And if you have PHP 8.0+, JIT can make CPU-heavy code 3-5x faster."
⚠️ THE MOST COMMON PRODUCTION MISS

Many production Laravel servers run with OpCache disabled. This means every single request recompiles every PHP file. Without OpCache, your Laravel app is 2-3x slower than it should be. It's the most common and easiest performance fix.

🔍 What is OpCache?

WITHOUT OPCACHE (EVERY REQUEST COMPILES) ═══════════════════════════════════════════════════════════════════ Request 1: PHP File → Read from Disk → Compile to Opcodes → Execute → Discard Request 2: PHP File → Read from Disk → Compile to Opcodes → Execute → Discard Request 3: PHP File → Read from Disk → Compile to Opcodes → Execute → Discard Time per request: 30-50ms (compilation) + execution Disk I/O: Heavy (reading PHP files every request) WITH OPCACHE (COMPILE ONCE, CACHE FOREVER) ═══════════════════════════════════════════════════════════════════ First Request: PHP File → Read from Disk → Compile to Opcodes → Store in RAM → Execute Request 2: Opcodes from RAM → Execute (NO compilation!) Request 3: Opcodes from RAM → Execute (NO compilation!) Time per request after first: 1-5ms (just execution) Disk I/O: Minimal (only for changed files) WHAT ARE OPCODES? ═══════════════════════════════════════════════════════════════════ PHP Source Code: OpCache: CPU: ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ <?php │ │ ZEND_ECHO │ │ Execute │ │ echo "Hello"; │ → │ ZEND_ADD │ → │ Opcodes │ │ $a = 1 + 2; │ │ ZEND_ASSIGN │ │ Directly │ │ ?> │ │ ... │ │ (FAST) │ └───────────────┘ └───────────────┘ └───────────────┘ OpCache stores the "middle representation" between source code and machine code.

📊 Performance Impact of OpCache

Scenario Without OpCache With OpCache Improvement
First request (after clear) 50ms 50ms Same (must compile once)
Subsequent requests (cold cache) 45ms 10ms 4.5x faster
Subsequent requests (warm cache) 45ms 3ms 15x faster
100,000 requests/day ~4,500 seconds CPU ~300 seconds CPU 93% reduction
THE BOTTOM LINE

OpCache is not optional for production. Without it, you're wasting 30-50ms per request on compilation. With it, your server can handle 3-4x more traffic with the same hardware.

⚙️ OpCache Configuration (php.ini)

MINIMUM PRODUCTION CONFIGURATION
; /etc/php/8.2/cli/conf.d/10-opcache.ini
; OR /etc/php/8.2/fpm/conf.d/10-opcache.ini

[opcache]
; Enable OpCache (MANDATORY)
opcache.enable=1
opcache.enable_cli=0  ; Not needed for CLI (artisan commands)

; Memory settings (adjust based on your app size)
opcache.memory_consumption=128    ; MB - Laravel needs ~64-128MB
opcache.interned_strings_buffer=16

; File system validation
opcache.validate_timestamps=0     ; In production, don't check file changes
; OR (if you deploy frequently)
opcache.validate_timestamps=1
opcache.revalidate_freq=60        ; Check every 60 seconds

; Max accelerated files (enough for Laravel)
opcache.max_accelerated_files=20000

; Memory efficiency
opcache.fast_shutdown=1
opcache.enable_file_override=0
opcache.optimization_level=0x7FFFBFFF

; JIT (PHP 8.0+)
opcache.jit=tracing
opcache.jit_buffer_size=100M
⚠️ CRITICAL: validate_timestamps

In production, set opcache.validate_timestamps=0 for maximum performance. But this means PHP will never see code changes until you restart PHP-FPM or clear OpCache.

Alternative: Set opcache.validate_timestamps=1 and opcache.revalidate_freq=60. This checks for file changes every 60 seconds.

Deployment best practice: Run php artisan opcache:clear or restart PHP-FPM after deployment.

🔥 PHP JIT (Just-In-Time Compilation)

WHAT IS JIT? ═══════════════════════════════════════════════════════════════════ Traditional PHP (without JIT): ┌─────────────────────────────────────────────────────────────────┐ │ PHP Source → OpCache (Opcodes) → Zend VM (Interpreter) → CPU │ │ ▲ │ │ │ │ │ ┌────────┴────────┐ │ │ │ SLOW (each │ │ │ │ opcode is │ │ │ │ interpreted) │ │ │ └─────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ PHP with JIT (PHP 8.0+): ┌─────────────────────────────────────────────────────────────────┐ │ PHP Source → OpCache → JIT Compiler → Native Machine Code → CPU│ │ │ ▲ │ │ │ │ │ │ ┌────────┴────────┐ ┌────┴────┐ │ │ │ Compiles hot │ │ FAST │ │ │ │ code paths to │ │ (C-like │ │ │ │ native machine │ │ speed) │ │ │ │ code │ └─────────┘ │ │ └─────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ JIT = Compile PHP code to machine code at runtime Best for: CPU-intensive operations (math, loops, image processing)
JIT IS NOT FOR EVERYONE

JIT helps CPU-bound code, not I/O-bound code. Laravel apps are typically I/O-bound (database, Redis, API calls). For CRUD apps, JIT provides little benefit. For image processing, encryption, or complex calculations, JIT can be 3-5x faster.

⚙️ JIT Configuration (PHP 8.0+)

JIT SETTINGS FOR PHP 8.0+
; /etc/php/8.2/cli/conf.d/10-opcache.ini

[opcache]
; Enable OpCache (JIT requires OpCache)
opcache.enable=1
opcache.memory_consumption=256     ; JIT needs more memory
opcache.jit_buffer_size=100M       ; JIT buffer (adjust based on app)

; JIT mode: tracing (recommended) or function
opcache.jit=tracing

; JIT optimization level (1205 is good default)
; 0 = disabled, 1-9 = more aggressive optimization
opcache.jit=1250

; Alternative JIT modes:
; opcache.jit=function   ; JIT only function bodies
; opcache.jit=tracing    ; JIT hot code paths (recommended)
; opcache.jit=disable    ; No JIT
CHECK IF JIT IS WORKING
# Run this command
php -i | grep JIT

# Expected output:
# opcache.jit => tracing => tracing
# opcache.jit_buffer_size => 100M => 100M

📊 OpCache + JIT Benchmark

Operation No OpCache OpCache Only OpCache + JIT Improvement
Laravel home page (CRUD) 65ms 15ms 14ms 4.3x faster (JIT minimal help)
Image resize (CPU heavy) 120ms 115ms 30ms 4x faster (JIT helps)
Array sort (1M items) 850ms 840ms 180ms 4.7x faster (JIT helps)
Encryption (AES-256) 45ms 44ms 12ms 3.7x faster (JIT helps)
REALITY CHECK

For 90% of Laravel apps (CRUD, API, admin panels), JIT won't make a noticeable difference. The bottleneck is database, not CPU. Focus on indexes, caching, and query optimization first. Enable OpCache, then consider JIT if you have CPU-intensive operations.

🛠️ OpCache Management Commands

USEFUL COMMANDS
# Check OpCache status
php -v  # Shows if OpCache is enabled

# Clear OpCache (after deployment)
sudo systemctl restart php8.2-fpm

# Or using Artisan (if package installed)
composer require laravel/octane  # Includes opcache:clear
php artisan opcache:clear

# Check OpCache statistics via web
# Create a route:
Route::get('/opcache-status', function () {
    if (!app()->isLocal()) {
        abort(404);
    }
    return '
' . print_r(opcache_get_status(), true) . '
'; });
LARAVEL DEPLOYMENT SCRIPT WITH OPCACHE
#!/bin/bash
# deploy.sh
php artisan down
git pull origin main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
# Clear OpCache so new code is compiled
sudo systemctl reload php8.2-fpm   # Graceful reload
# OR
# sudo systemctl restart php8.2-fpm  # Full restart (faster but kills active requests)
php artisan up

🔧 Troubleshooting OpCache

COMMON PROBLEMS
VERIFY OPCACHE IS WORKING
# Create a test file
echo '<?php var_dump(opcache_get_status(false));' | php

# Look for:
# ["opcache_enabled"]=> bool(true)
# ["memory_usage"]["used_memory"] => int(12345678)
# ["opcache_statistics"]["num_cached_scripts"] => int(342)

📝 Topic 17 Summary: OpCache & PHP JIT

Feature Purpose When to Enable Performance Gain
OpCache Cache compiled PHP opcodes ALWAYS in production 3-5x faster for PHP execution
OpCache + JIT (tracing) Compile hot paths to machine code CPU-intensive apps only 3-5x faster for CPU operations
OpCache + JIT (function) JIT entire functions Very specific use cases Variable
📌 THE RULE: Enable OpCache in production. Always. It's free performance. For JIT, measure first. If your app is CPU-bound (image processing, encryption, complex calculations), enable JIT. If it's a typical CRUD app, OpCache alone is enough.
NEXT TOPIC PREVIEW

Topic 18: Composer Autoloader Optimization — classmap-authoritative vs optimize-autoloader. How to make PHP stop searching for files and save 5-10ms per request.