PHP: RFC:JIT

tosh1 pts0 comments

PHP: rfc:jit

rfc:jit

PHP RFC: JIT

Version: 1.0

Date: 2019-01-28

Author: Dmitry Stogov dmitry@php.net, Zeev Suraski zeev@php.net

Status: Implemented (PHP 8.0)

First Published at: https://wiki.php.net/rfc/jit

Introduction

It's no secret that the performance jump of PHP 7 was originally initiated by attempts to implement JIT for PHP. We started these efforts at Zend (mostly by Dmitry) back in 2011 and since that time tried 3 different implementations. We never moved forward to propose to release any of them, for three main reasons: They resulted in no substantial performance gains for typical Web apps; They were complex to develop and maintain; We still had additional directions we could explore to improve performance without having to use JIT.

The Case for JIT Today

Even though most of the fundamentals for JIT-enabling PHP haven't changed - we believe there is a good case today for JIT-enabling PHP.

First, we believe we've reached the extent of our ability to improve PHP's performance using other optimization strategies. In other words - we can't further improve the performance of PHP unless we use JIT.

Secondly - using JIT may open the door for PHP being more frequently used in other, non-Web, CPU-intensive scenarios - where the performance benefits will actually be very substantial - and for which PHP is probably not even being considered today.

Lastly - making JIT available can provide us (with additional efforts) with the ability to develop built-in functions in PHP, instead of (or in addition to) C - without suffering the huge performance penalty that would be associated with such a strategy in today's, non-JITted engine. This, in turn, can open the door to faster innovation - and also more secure implementations, that would be less susceptible to memory management, overflows and similar issues associated with C-based development.

Proposal

We propose to include JIT in PHP 8 and provide additional efforts to increase its performance and usability.

In addition, we propose to consider including JIT in PHP 7.4 as an experimental feature (disabled by default).

PHP JIT is implemented as an almost independent part of OPcache. It may be enabled/disabled at PHP compile time and at run-time.<br>When enabled, native code of PHP files is stored in an additional region of the OPcache shared memory and op_array->opcodes[].handler(s) keep pointers to the entry points of JIT-ed code. This approach doesn't require engine modification at all.

We use DynAsm (developed for LuaJIT project) for generation of native code. It's a very lightweight and advanced tool, but does assume good, and very low-level development knowledge of target assembler languages. In the past we tried LLVM, but its code generation speed was almost 100 times slower, making it prohibitively expensive to use. Currently we support x86 and x86_64 CPUs on POSIX platforms and Windows. DynAsm also supports ARM. ARM64, MIPS, MIPS64 and PPC, so in theory we should be able to support all of the platforms that are popular for PHP deployments (given enough efforts).

PHP JIT doesn't introduce any additional IR (Intermediate Representation) form. It generates native code directly from PHP byte-code and information collected by SSA static analyses framework (a part of opcache optimizer). Code is usually generated separately for each PHP byte-code instruction. Only few combinations are considered together (e.g. compare + conditional jump).

If type of PHP variable is exactly inferred (in SSA) to LONG or DOUBLE, and it can't be accessed indirectly, JIT may store its value directly in CPU registers, avoiding memory stores and loads. PHP JIT liner-scan register allocation algorithm, tat combines high speed with reasonable quality.

The quality of the JIT may be demonstrated on Mandelbrot benchmark published at https://gist.github.com/dstogov/12323ad13d3240aee8f1, where it improves performance more than 4 times (0.011 sec vs 0.046 sec on PHP 7.4).

function iterate($x,$y)<br>$cr = $y-0.5;<br>$ci = $x;<br>$zr = 0.0;<br>$zi = 0.0;<br>$i = 0;<br>while (true) {<br>$i++;<br>$temp = $zr * $zi;<br>$zr2 = $zr * $zr;<br>$zi2 = $zi * $zi;<br>$zr = $zr2 - $zi2 + $cr;<br>$zi = $temp + $temp + $ci;<br>if ($zi2 + $zr2 > BAILOUT)<br>return $i;<br>if ($i > MAX_ITERATIONS)<br>return 0;

The following is the complete assembler code generated for the PHP function above, with the main loop code visible between .L5 and .L7:

JIT$Mandelbrot::iterate: ; (/home/dmitry/php/bench/b.php)<br>sub $0x10, %esp<br>cmp $0x1, 0x1c(%esi)<br>jb .L14<br>jmp .L1<br>.ENTRY1:<br>sub $0x10, %esp<br>.L1:<br>cmp $0x2, 0x1c(%esi)<br>jb .L15<br>mov $0xec3800f0, %edi<br>jmp .L2<br>.ENTRY2:<br>sub $0x10, %esp<br>.L2:<br>cmp $0x5, 0x48(%esi)<br>jnz .L16<br>vmovsd 0x40(%esi), %xmm1<br>vsubsd 0xec380068, %xmm1, %xmm1<br>.L3:<br>mov 0x30(%esi), %eax<br>mov 0x34(%esi), %edx<br>mov %eax, 0x60(%esi)<br>mov %edx, 0x64(%esi)<br>mov 0x38(%esi), %edx<br>mov %edx, 0x68(%esi)<br>test $0x1, %dh<br>jz .L4<br>add $0x1, (%eax)<br>.L4:<br>vxorps %xmm2, %xmm2, %xmm2<br>vxorps %xmm3, %xmm3, %xmm3<br>xor %edx, %edx<br>.L5:<br>cmp $0x0, EG(vm_interrupt)<br>jnz .L18<br>add...

code performance additional dmitry efforts today

Related Articles