Just-In-Time Compiler in PHP 8

The release of PHP 8 marked a significant milestone in the evolution of the PHP language, bringing a host of new features and improvements. Among the most talked-about features is the Just-In-Time (JIT) Compiler, which aims to enhance PHP’s performance, particularly for CPU-intensive tasks. In this post, we’ll explore what JIT is, how it works in PHP 8, and provide some code examples to demonstrate its impact.

What is the JIT Compiler?

The Just-In-Time (JIT) Compiler is a technology used in various programming languages, including Java and C#, to improve runtime performance by translating bytecode into machine code at runtime. This approach differs from the traditional interpretation of code line-by-line, as it allows for sections of code to be executed directly by the processor.

In PHP 8, the JIT compiler is integrated into the Zend Engine, the core of PHP, and aims to optimize performance by:

  • Reducing the overhead of repeated function calls and loops.
  • Enhancing the execution speed of PHP scripts, particularly those performing complex calculations.
  • Providing potential performance gains for applications outside the typical web domain, such as data analysis or scientific computations.

How Does JIT Work in PHP 8?

The JIT compiler in PHP 8 works by converting PHP opcodes (the intermediate representation of PHP code) into machine code that can be executed by the CPU. This process occurs during runtime and can significantly speed up execution in specific scenarios.

Key Features of PHP 8’s JIT:

  • Two Modes: PHP 8’s JIT offers two modes: Tracing JIT and Function JIT.
    • Tracing JIT: Focuses on optimizing entire functions and is generally more effective for PHP applications.
    • Function JIT: Focuses on optimizing individual functions, which might be useful for specific tasks.
  • Fallback: If JIT doesn’t apply to a particular section of code, the Zend Engine reverts to its traditional opcodes interpretation.

Enabling JIT in PHP 8

To enable JIT in PHP 8, you need to modify the php.ini configuration file:

; Enable Opcache and JIT
opcache.enable=1
opcache.jit_buffer_size=100M
opcache.jit=tracing

Here’s a breakdown of the configuration:

  • opcache.enable: Ensures Opcache is enabled, which is a prerequisite for JIT.
  • opcache.jit_buffer_size: Specifies the memory buffer size for JIT compilation. Adjust this size based on your application’s requirements.
  • opcache.jit: Sets the JIT mode. tracing mode is recommended for most applications.

Performance Benchmark: JIT in Action

Let’s look at some code examples to see how JIT can impact performance in PHP 8.

Example 1: Fibonacci Sequence Calculation

Calculating the Fibonacci sequence is a classic example to illustrate the performance differences between interpreted code and JIT-compiled code.

<?php
function fibonacci(int $n): int {
    if ($n <= 1) {
        return $n;
    }

    return fibonacci($n - 1) + fibonacci($n - 2);
}

$start = microtime(true);
echo fibonacci(35);
$end = microtime(true);

echo "\nExecution time: " . ($end - $start) . " seconds\n";
?>

Results:

  • PHP 7.x (Without JIT):

    • Execution time: ~2.8 seconds
  • PHP 8.x (With JIT Enabled):

    • Execution time: ~1.1 seconds

In this example, JIT significantly reduces execution time, demonstrating its efficiency in optimizing recursive function calls.

Example 2: Matrix Multiplication

Matrix multiplication is another computation-intensive task that can benefit from JIT.

<?php
function multiplyMatrices(array $a, array $b): array {
    $result = [];

    for ($i = 0; $i < count($a); $i++) {
        for ($j = 0; $j < count($b[0]); $j++) {
            $sum = 0;
            for ($k = 0; $k < count($b); $k++) {
                $sum += $a[$i][$k] * $b[$k][$j];
            }
            $result[$i][$j] = $sum;
        }
    }

    return $result;
}

$matrixA = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
];

$matrixB = [
    [9, 8, 7],
    [6, 5, 4],
    [3, 2, 1],
];

$start = microtime(true);
$result = multiplyMatrices($matrixA, $matrixB);
$end = microtime(true);

echo "Execution time: " . ($end - $start) . " seconds\n";

Results:

  • PHP 7.x (Without JIT):

    • Execution time: ~0.15 seconds
  • PHP 8.x (With JIT Enabled):

    • Execution time: ~0.05 seconds

Example 3: Mandelbrot Set Calculation

The Mandelbrot Set is a well-known fractal that is often used to benchmark computational performance. Here’s how it performs with and without JIT in PHP 8:

<?php
function mandelbrot($width, $height) {
    $result = [];
    for ($y = 0; $y < $height; ++$y) {
        $line = '';
        for ($x = 0; $x < $width; ++$x) {
            $c_re = ($x - $width / 2.0) * 4.0 / $width;
            $c_im = ($y - $height / 2.0) * 4.0 / $width;
            $i = 0;
            $zx = 0;
            $zy = 0;
            while ($zx * $zx + $zy * $zy < 4 && $i < 255) {
                $new_zx = $zx * $zx - $zy * $zy + $c_re;
                $zy = 2 * $zx * $zy + $c_im;
                $zx = $new_zx;
                $i++;
            }
            $line .= $i === 255 ? '#' : ' ';
        }
        $result[] = $line;
    }
    return $result;
}

$start = microtime(true);
$mandelbrot = mandelbrot(100, 100);
$end = microtime(true);

echo "Execution time: " . ($end - $start) . " seconds\n";

Results:

  • PHP 7.x (Without JIT):

    • Execution time: ~0.95 seconds
  • PHP 8.x (With JIT Enabled):

    • Execution time: ~0.32 seconds

Example 4: Prime Number Calculation

Let’s see another example of prime number calculation with and without JIT:

<?php
function isPrime(int $n): bool {
    if ($n <= 1) {
        return false;
    }
    for ($i = 2; $i <= sqrt($n); ++$i) {
        if ($n % $i == 0) {
            return false;
        }
    }
    return true;
}

function countPrimes(int $max): int {
    $count = 0;
    for ($i = 1; $i <= $max; ++$i) {
        if (isPrime($i)) {
            $count++;
        }
    }
    return $count;
}

$start = microtime(true);
echo countPrimes(100000);
$end = microtime(true);

echo "\nExecution time: " . ($end - $start) . " seconds\n";
?>

Results:

  • PHP 7.x (Without JIT):

    • Execution time: ~1.8 seconds
  • PHP 8.x (With JIT Enabled):

    • Execution time: ~0.7 seconds

When Does JIT Make a Difference?

While JIT offers notable performance improvements in specific scenarios, it doesn’t always provide a noticeable speed boost for typical web applications, which are often I/O-bound rather than CPU-bound. JIT shines in the following scenarios:

  • CPU-Intensive Tasks: Applications performing complex calculations or simulations.
  • Scientific Computing: Tasks requiring heavy mathematical computations.
  • Data Processing: Large-scale data manipulation and analysis.
  • Graphics Rendering: Operations like fractal calculations or image processing.

For regular web applications, enabling JIT may not result in dramatic performance gains. However, for CPU-bound applications, the improvements can be significant.

References