Innovate faster and cut risk with PHP experts from Zend Services.
Beginning to advanced PHP classes to learn and earn global certification.
Help me choose >
Submit support requests and browse self-service resources.
Matthew Weier O’Phinney
With the release of PHP 8, PHP JIT compilation has become a big point of discussion. But, as we discuss in this article, the JIT compiler in PHP 8 is more of a PHP milestone than a performance fix-all.
In this blog, we’ll discuss the origins of PHP JIT compilation, how it works in PHP 8, what it brings to the language, and what it may be used for in the future.
Each script processed in a request to a PHP web application is parsed, and then compiled into "byte codes". Byte codes, also called "op codes", are pseudo-instructions that a virtual machine can process. When you run `php -v` or `php -i` at the command line, or execute `phpinfo()`, you'll see mention of the "Zend Engine"; this is a virtual machine, similar to LLVM or the JVM.
Programming languages that use a VM compile their language to byte codes the VM understands. A virtual machine's job is to take those byte codes, compile them to machine code, and then execute them.
PHP 5.5 introduced the OPcache. When the OPcache is enabled, PHP takes the byte codes it compiles to and stores them in the OPcache before passing them to the VM. On subsequent requests, it checks to see if it has an entry for that script in the OPcache; if so, it passes those directly to the VM, thus skipping the steps of parsing and compiling to opcodes. This helps improve performance.
It's worth noting that the OPcache is memory-limited; it can only store up to a configured amount before it either stops caching, or starts pushing out unused opcodes.
Even with an OPcache, there are bottlenecks in many applications. As an example, applications using most modern PHP frameworks tend to be bootstrap-heavy; the logic required to gather configuration, seed a dependency injection container, and otherwise initialize the application often account for up to 25% of every request.
To combat this, PHP 7.4 introduced what is known as "opcache preloading". Essentially, this is a mechanism that allows administrators to specify scripts that should be pre-compiled into the opcache during language startup.
When using Apache with mod_php or using php-fpm, a pool of workers is spawned and initialized as the server starts, and these then consult the opcache preload rules, load those scripts, compile them to byte codes, and cache the byte codes.
This saves time immediately, as there are no longer race conditions as the server is initially accessed by end users. Again, though, it is memory-limited. And, in fact, there's a tipping point where adding too many entries to the preload list either has no benefit, or even can negatively impact performance.
When we looked originally at the OPcache, we found it removed the parsing and compilation steps that gave us opcodes for the VM. However, the VM still needed to compile these to native machine code.
What JIT does is allow an extra layer of caching: caching of the native machine code. When JIT has handled code previously, we go directly from identifying the script to use, to executing the native machine code.
This has the potential to be insanely fast!
The first approach, and one still available in the engine (albeit in another form), is "function"-based JIT. The original approach required users to mark code that should be passed to the JIT. From there, it would do its thing. Today, it instead analyzes each function, attempting to optimize and compile each path through it.
The problem is, in real-world applications, such as WordPress, it doesn't really help. Ironically, it adds overhead, which decreases the performance gains found in PHP 7.4
JIT compilation in PHP 8 introduces another JIT compilation approach, this time using “traces.”
With this approach, as the JIT encounters code, it creates a "trace", which includes the actual VM instructions (opcodes), as well as additional runtime information such as the types of operands used, what classes were in the context, and what functions, methods, or closures were called.
Based on the how often a trace is called, how long it is, and what operations it performs, the JIT decides if the code can benefit from compilation.
The JIT Compilation approach in PHP 8 means significantly better performance for numerical calculations and slightly better performance for typical PHP web applications.
PHP Benchmark Suite
JIT compilation also unlocks the potential to move code from C to PHP, because PHP is now sufficiently fast.
The bad news is that JIT mainly benefits the sorts of applications we do not write in PHP -- stuff like crunching large datasets or graphics manipulation.
PHP's sweet spot has been as a glue language for the web: taking data from a variety of sources and spitting out a web page or API payload. But, as I talk about in the next section, JIT compilation may be changing the way we use PHP.
When a language is difficult to write or requires a compilation step to test, it restricts the number of people who can develop. When the language is not fast enough, it's not ideal for large or complex data sets. In order to work well with those types of data sets, the language needs a combination of ease of writing and raw computational power.
Because JIT works best with CPU-intensive processing, and PHP is considered one of the easier languages to learn because of its lightweight syntax, I think we’ll see PHP become a major player in machine learning and big data ecosystems.
To start, I think we’ll see existing machine learning libraries for PHP such as Rubix ML or PHP-ML take advantage of JIT and start making inroads into the machine learning ecosystem, particularly due to the ease with which they can be incorporated into web services.
The JIT compiler, as present in PHP 8, doesn’t solve performance problems for a large set of applications. However, it does open new doors for PHP as a language — especially when applied to data heavy disciplines like machine learning.
For additional information on the JIT compiler in PHP 8, be sure to check out my recent webinar below. It goes over the content in this blog in more depth, and dives in on more of the potential benefits of JIT — like moving code from C to PHP or improving the performance of static analysis tools.
Still looking for more on the JIT compiler and PHP 8? Be sure to check out these resources:
Development Lead for the Laminas Project, Zend by Perforce
Matthew began developing on Zend Framework (ZF) before its first public release, and led the project for Zend from 2009 through 2019. He is a founding member of the PHP Framework Interop Group (PHP-FIG), which creates and promotes standards for the PHP ecosystem — and is serving his second elected term on the PHP-FIG Core Committee.