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. | Value | Description |
---|---|---|
1. | disable | JIT is completely disabled and cannot be enabled at run time. |
2. | off | JIT is disabled but can be enabled at run time. |
3. | on or tracing | Enables the JIT in tracing mode. This is the same as CRTO = 1254. |
4. | function | Enables 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):
Value | Description |
---|---|
0 | CPU-specific optimization is disabled. |
1 | AVX is enabled, if the CPU supports it. |
R (Register allocation):
Value | Description |
---|---|
0 | Register allocation is not performed. |
1 | Block-local register allocation is performed. |
2 | Global register allocation is performed. |
T (Trigger):
Value | Description |
---|---|
0 | All functions are compiled on script load. |
1 | Functions are compiled on first execution. |
2 | Functions are profiled on first request and hot functions are compiled on second request. |
3 | Functions are profiled at run time and hot functions are compiled. |
4 | Currently not used. |
5 | Tracing JIT is used. Functions are profiled at run time and traces for hot code segments are compiled. |
O (Optimization level):
Value | Description |
---|---|
0 | JIT is not used. |
1 | Minimal JIT is used. |
2 | Selective VM handler inlining is used. |
3 | Type inference is used. |
4 | Call graph is used. |
5 | Whole 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. | Case | Configuration |
---|---|---|
1. | No OPcache | zend_extension=opcache opcache.enable=0 opcache.enable_cli=0 |
2. | No JIT | zend_extension=opcache opcache.enable=1 opcache.enable_cli=1 opcache.jit_buffer_size=0 |
3. | Function JIT | zend_extension=opcache opcache.enable=1 opcache.enable_cli=1 opcache.jit_buffer_size=128M opcache.jit=function |
4. | Tracing JIT | zend_extension=opcache opcache.enable=1 opcache.enable_cli=1 opcache.jit_buffer_size=128M opcache.jit=tracing |
We get the following results:
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