Just-In-Time (JIT) Compilation in PHP 8.0

Just-In-Time (JIT) Compilation in PHP 8.0

Just-in-time (JIT) compilation is a process that takes compilation of the code at run time. JIT compilation allows improving performance of interpreted programming languages.

Since PHP 8.0, we can use JIT. To use JIT, the OPcache extension must be installed and enabled. OPcache extension allows eliminating the need to load and parse scripts on every request by storing precompiled script bytecode in shared memory.

Enabling OPcache

The zend_extension directive can be used to load the OPcache extension into PHP. We can set zend_extension directive in php.ini file.

zend_extension=opcache

OPcache can be enabled by using opcache.enable directive. This directive does not enable OPcache for the CLI version of PHP. To do that, need to use a separate opcache.enable_cli directive.

opcache.enable=1
opcache.enable_cli=1

The opcache_get_status function can be used to check whether OPcache is enabled. This function returns status information of the OPcache.

<?php

function isOpcacheEnabled(): bool
{
    if (!function_exists('opcache_get_status')) {
        return false;
    }

    return !empty(opcache_get_status()['opcache_enabled']);
}

echo isOpcacheEnabled() ? 'Enabled' : 'Disabled';

Configuring JIT

After OPcache have enabled, we can start to configure JIT. The opcache.jit_buffer_size directive defines how much shared memory can be reserved for compiled JIT code. Value can be specified as integer in bytes or using shorthand notation with data size suffixes (K, M or G). Default value is 0. This means that JIT is disabled.

opcache.jit_buffer_size=128M

To check whether JIT is enabled, the opcache_get_status function can be used.

<?php

function isJitEnabled(): bool
{
    if (!function_exists('opcache_get_status')) {
        return false;
    }

    return !empty(opcache_get_status()['jit']['enabled']);
}

echo isJitEnabled() ? 'Enabled' : 'Disabled';

The opcache.jit directive controls JIT behavior. Default value is tracing.

opcache.jit=tracing

This directive accepts the following values:

No.ValueDescription
1.disableJIT is completely disabled and cannot be enabled at run time.
2.offJIT is disabled but can be enabled at run time.
3.on or tracingEnables the JIT in tracing mode. This is the same as CRTO = 1254.
4.functionEnables the JIT in function mode. This is the same as CRTO = 1205.
5.4-digit integer (CRTO)Each digit is used to define:
1. CPU-specific optimization flags (C)
2. Register allocation (R)
3. Trigger (T)
4. Optimization level (O)

The following values are available for C, R, T, and O positions represented as digits:

C (CPU-specific optimization flags):

ValueDescription
0CPU-specific optimization is disabled.
1AVX is enabled, if the CPU supports it.

R (Register allocation):

ValueDescription
0Register allocation is not performed.
1Block-local register allocation is performed.
2Global register allocation is performed.

T (Trigger):

ValueDescription
0All functions are compiled on script load.
1Functions are compiled on first execution.
2Functions are profiled on first request and hot functions are compiled on second request.
3Functions are profiled at run time and hot functions are compiled.
4Currently not used.
5Tracing JIT is used. Functions are profiled at run time and traces for hot code segments are compiled.

O (Optimization level):

ValueDescription
0JIT is not used.
1Minimal JIT is used.
2Selective VM handler inlining is used.
3Type inference is used.
4Call graph is used.
5Whole script is optimized.

In most cases, recommended using the tracing JIT.

The opcache_get_configuration function can be used to determine mode of the JIT (tracing, function, etc.). This function returns configuration information of the OPcache.

<?php

function getJitMode(): string
{
    if (!function_exists('opcache_get_configuration')) {
        return '';
    }

    return opcache_get_configuration()['directives']['opcache.jit'];
}

echo getJitMode();

Debugging JIT

The opcache.jit_debug directive controls JIT debug output. Default value is 0. This means debug output is disabled. This directive accepts a bit-mask that specifies which debug output features must be enabled.

For example, a bit-mask 1<<3 (8) prints the assembly code stubs.

php -d opcache.jit_debug=8 test.php
JIT$$interrupt_handler: ; (unknown)
        mov %gs:0x58, %rax
        mov 0x50(%rax), %rax
        mov 0x8(%rax), %rax
        mov $0x0, 0x2962(%rax)
        mov %gs:0x58, %rax
        mov 0x50(%rax), %rax
...

Note: -d option allows setting INI config for certain script during execution.

Testing JIT

For testing, we use a function that calculates the nth number in the Fibonacci sequence. The Fibonacci sequence is the series of numbers where each subsequent number is equal to the sum of the two previous numbers.

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...

We calculate the 40th number in the Fibonacci sequence and measure execution time of a script.

<?php

function fib(int $number): int
{
    if ($number <= 1) {
        return $number;
    }

    return fib($number - 1) + fib($number - 2);
}

$startTime = microtime(true);
echo fib(40).PHP_EOL;
$endTime = microtime(true);

echo sprintf('%.2f sec', $endTime - $startTime);

For testing, we use 4 cases with the following configuration:

No.CaseConfiguration
1.No OPcachezend_extension=opcache
opcache.enable=0
opcache.enable_cli=0
2.No JITzend_extension=opcache
opcache.enable=1
opcache.enable_cli=1
opcache.jit_buffer_size=0
3.Function JITzend_extension=opcache
opcache.enable=1
opcache.enable_cli=1
opcache.jit_buffer_size=128M
opcache.jit=function
4.Tracing JITzend_extension=opcache
opcache.enable=1
opcache.enable_cli=1
opcache.jit_buffer_size=128M
opcache.jit=tracing

We get the following results:

Average Execution Time (JIT Testing)

As we can see, the best results achieved using the tracing JIT.

JIT can provide significant performance gain for applications that performs CPU-intensive calculations. In many web applications the bottleneck is database, network or I/O. In these applications, the impact of JIT can be minimal or even negative due to the overhead of profiling and compilation at run time. Impact of JIT depends on application. Each application should be tested individually.

Leave a Comment

Cancel reply

Your email address will not be published.