Slide 1

Slide 1 text

PHP 8 & PHP 8 & PHP 8 & JIT Compilation JIT Compilation JIT Compilation Benoit Jacquemont Benoit Jacquemont Benoit Jacquemont @bjacquemont @bjacquemont @bjacquemont

Slide 2

Slide 2 text

PHP Perf Evolution Source: https://kinsta.com/blog/php-benchmarks/

Slide 3

Slide 3 text

How To Get Even Further? JIT As The Next Frontier?

Slide 4

Slide 4 text

What's JIT?

Slide 5

Slide 5 text

Just In Time Compilation It's a way of executing computer code that involves compilation at run time rather than prior to execution. Expectation Compiled code speed > > Interpreted code speed

Slide 6

Slide 6 text

Platforms With JIT Java with the Hotspot JVM .NET with the Common Language Runtime NodeJS with V8 ...

Slide 7

Slide 7 text

Once upon a time, there were PHP and JIT...

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

Compilation Is A Very CPU Intensive Process 10 minutes to Compile Zend Engine 1 hour and half to compile the Linux kernel

Slide 11

Slide 11 text

So, You Want To Make Your Code Execute Faster By Compiling Stuff During Execution?

Slide 12

Slide 12 text

Fast Compilation Time And Best Bene ts From Compilation Compile Only The Most Executed Code Less code to compile = time spent on compilation Most used code compiled = relevant performance improvements

Slide 13

Slide 13 text

How To Know What Is The Most Executed Code Parts? Add A Pro ler Into The Mix...

Slide 14

Slide 14 text

JIT Standard Work ow Initial code ⤚ ⚙ syntax validation + compilation ⚙ → intermediate representation ⤚ ⚙ execution + pro ling ⚙ → selection of most used code ⤚ ⚙ compilation to native code ⚙ → native code for most used code ➠ execution on the processor

Slide 15

Slide 15 text

Executing Native Code The Hardware Problem Native means built for a processor instructions set. And there's more than one... x86 x86_64 ARM MIPS RISC V

Slide 16

Slide 16 text

Executing Native Code The OS Problem The OS controls what is executed. Should work on Linux, but as well as Windows, MacOS and BSDs, 32bits and 64bits...

Slide 17

Slide 17 text

PHP JIT Requirements an internal pro ler very fast compilers from Opcode to: x86 x86_64 ARM MIPS ... Multi-OS support

Slide 18

Slide 18 text

Introducing Introducing Introducing DynASM DynASM DynASM Avoiding the Not Invented Here syndrom

Slide 19

Slide 19 text

DynASM DynASM is a Dynamic Assembler Developped for LuaJIT

Slide 20

Slide 20 text

DynASM Is A Generic Assembler!

Slide 21

Slide 21 text

Without DynASM Need to generate each of the following x86_64 ARM MIPS $i++; mov ebx, 0x1234h mov eax, [ebx] inc eax mov [ebx], eax MOV R0, (#0x1234h) ADD R0, R0, #1 MOV (#0x1234h), R0 lw $t0,0x1234h addw $t0,$t0,1 sw $t0,0x1234h

Slide 22

Slide 22 text

With DynASM Only need to generate one assembly code DynASM will generate the native code for the target x86_64 ARM MIPS $i++; mov $0x1234, %rdi inc %rdi mov %rdi, $0x1234 mov ebx, 0x1234h mov eax, [ebx] inc eax mov [ebx], eax MOV R0, (#0x1234h) ADD R0, R0, #1 MOV (#0x1234h), R0 lw $t0,0x1234h addw $t0,$t0,1 sw $t0,0x1234h

Slide 23

Slide 23 text

DynASM Is Fast Very fast and lightweight "assembler". (100x faster than LLVM)

Slide 24

Slide 24 text

JIT Compilation & PHP Instead of developing multiple compilers: PHP opcode to x86 PHP opcode to x86_64 PHP opcode to ARM PHP opcode to MIPS ... Only need: PHP opcode to DynASM Assembly

Slide 25

Slide 25 text

PHP JIT Work ow PHP code ⤚ ⚙ syntax validation + compilation ⚙ → opcode ⤚ ⚙ execution + pro ling ⚙ → selection of most used code ⤚ ⚙ compilation to DynASM assembly ⚙ → DynASM Assembly ⤚ ⚙ compilation to native code (thanks DynASM!) ⚙ → Native code ➠ execution on the processor

Slide 26

Slide 26 text

JIT Implementation In PHP8

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

PHP JIT Is An Extension Of The Opcode Cache

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

Let's Compile!

Slide 41

Slide 41 text

Hello World! Opcode

Slide 42

Slide 42 text

Hello World! DynASM Assembly

Slide 43

Slide 43 text

If Opcode $a = true; if ($a === true) { echo "Yes!"; } else { echo "No!"; } $_main: L0 (3): ASSIGN CV0 bool(true) L1 (5): T1 = IS_IDENTICAL CV0 bool(true) L2 (5): JMPZ T1 L5 L3 (6): ECHO string("Yes!") L4 (10): RETURN int(1) L5 (8): ECHO string("No!") L6 (10): RETURN int(1)

Slide 44

Slide 44 text

$a = true; if ($a === true) { echo "Yes!"; } else { echo "No!"; } sub $0x10, %rsp lea 0x50(%r14), %rdi cmp $0xa, 0x8(%rdi) jnz .L1 mov (%rdi), %rdi cmp $0x0, 0x18(%rdi) jnz .L7 add $0x8, %rdi .L1: test $0x1, 0x9(%rdi) jnz .L8 .L2: mov $0x3, 0x8(%rdi) .L3: mov $EG(exception), %rax cmp $0x0, (%rax) jnz JIT$$exception_handler lea 0x50(%r14), %rdi cmp $0xa, 0x8(%rdi) jnz .L4 mov (%rdi), %rdi add $0x8, %rdi .L4: cmp $0x3, 0x8(%rdi) jz .L5 jmp .L6 .L5: add $0x60, %r15 mov %r15, (%r14) mov $0x40af2d48, %rdi mov $0x4, %rsi mov $php_output_write, %rax call *%rax mov $EG(exception), %rax cmp $0x0, (%rax) jnz JIT$$exception_handler add $0x20, %r15 add $0x10, %rsp mov $0x559ee5a027b1, %rax call *%rax jmp (%r15) .L6: mov $0x4115d6a0, %r15 mov %r15, (%r14) mov $0x40af2d70, %rdi mov $0x3, %rsi mov $php_output_write, %rax call *%rax mov $EG(exception), %rax cmp $0x0, (%rax) jnz JIT$$exception_handler add $0x20, %r15 add $0x10, %rsp mov $0x559ee5a027b1, %rax call *%rax jmp (%r15) .L7: mov $0x4115d5c0, %rsi mov $zend_jit_assign_const_to_typed_ref, %rax call *%rax jmp .L3 .L8: mov (%rdi), %rax sub $0x1, (%rax) jnz .L9 mov %rax, (%rsp) mov $0x3, 0x8(%rdi) mov (%rsp), %rdi mov %r15, (%r14) mov $rc_dtor_func, %rax call *%rax jmp .L3 .L9: mov (%rdi), %rax mov 0x4(%rax), %eax and $0xfffffc10, %eax cmp $0x10, %eax jnz .L2 mov %rdi, (%rsp) mov (%rdi), %rdi mov $gc_possible_root, %rax call *%rax mov (%rsp), %rdi jmp .L2

Slide 45

Slide 45 text

JIT Con guration

Slide 46

Slide 46 text

JIT Buffer At 0 , JIT disabled (default value) opcache.jit_buffer_size=100M

Slide 47

Slide 47 text

JIT Controls Aka CRTO C: CPU Optimization 0 - none 1 - enable AVX instruction generation R: Register Allocation 0 - don't perform register allocation 1 - use local liner-scan register allocator 2 - use global liner-scan register allocator T: JIT Trigger 0 - JIT all functions on rst script load 1 - JIT function on rst execution 2 - Pro le on rst request and compile hot functions on second request 3 - Pro le on the y and compile hot functions 4 - Compile functions with @jit tag in doc-comments O: Optimization level 0 - don't JIT 1 - minimal JIT (call standard VM handlers) 2 - selective VM handler inlining 3 - optimized JIT based on static type inference of individual function 4 - optimized JIT based on static type inference and call tree 5 - optimized JIT based on static type inference and inner procedure analyses opcache.jit=1235

Slide 48

Slide 48 text

How To Run PHP8 JIT With Docker Compile It From / docker run akondas/php:8.0-cli-alpine \ php -d zend_extension=opcache.so \ -d opcache.enable_cli=1 \ -d opcache.jit_buffer_size=100M \ -d opcache.jit=1235 github.com/zendtech/php-src/tree/jit-dynasm

Slide 49

Slide 49 text

How To Dump DynASM Assembly opcache.jit_debug=1

Slide 50

Slide 50 text

STOP WITH THE SUSPENS! SHOW ME THE PERFORMANCES!!

Slide 51

Slide 51 text

Zend/Bench.Php Very basic bench available in the PHP source tree Without JIT: 0.567s With JIT: 0.130s x4 improvement

Slide 52

Slide 52 text

Fibonacci Without JIT: 8.3s With JIT: 2.7s x3 improvement function fibonacci($n){ return(($n < 2) ? 1 : fibonacci($n - 2) + fibonacci($n - 1)); } $start = microtime(true); fibonacci(40); $stop = microtime(true); echo sprintf("Time: %s\n", $stop - $start);

Slide 53

Slide 53 text

Real-Life Performance Results Aka Let's Crush Your Dreams

Slide 54

Slide 54 text

Composer Benchmark composer update on Akeneo PIM Enterprise Edition Without JIT: 53s With JIT: Oops... JIT can have an effect on application behavior Your requirements could not be resolved to an installable set of packages Problem 1 - akeneo/pim-community-dev 3.2.x-dev requires doctrine/annotations 1.6.0 -> satisfiable by doctrine/annotations[v1.6.0]. - Conclusion: don't install doctrine/annotations v1.1.2| remove doctrine/annotations v1.6.0

Slide 55

Slide 55 text

Akeneo PIM EE Installation Time Without JIT: 2m34s with JIT: 2m37s

Slide 56

Slide 56 text

Wordpress Front Page Without JIT: 190 requests/s with JIT CTRO 1235: 160 requests/s with JIT CTRO 1225: 189 requests/s

Slide 57

Slide 57 text

But Why JIT Can't Give Us More Perfz?

Slide 58

Slide 58 text

IO Bound Vs CPU Bound In general, application perf limited either by CPU or by IO (database, network, disk, etc...) Most Of The Time, PHP Applications Are IO Bound

Slide 59

Slide 59 text

Native Functions Are Very Fast PHP is maybe the fastest scripting language Large number of native functions. All written in C. Native functions already natively compiled to machine code.

Slide 60

Slide 60 text

GFX PHP Pure PHP image manipulation library 1.4MB PNG rescale 20x Without JIT: 52s With JIT: 38s 27% faster

Slide 61

Slide 61 text

PHP Engine Dev State Estimated 5 millions PHP devs worldwide 4 active devs on PHP Zend Engine C makes it dif cult for PHP devs to work on it github.com/php/php-src/graphs/contributors

Slide 62

Slide 62 text

What If PHP Internal Could Be Written In... PHP If JIT Could Make PHP As Fast As C

Slide 63

Slide 63 text

GFX PHP JIT Vs GD 1.4MB PNG rescale 20x Without JIT: 52s With JIT: 38s Same with PHP+GD: 0.9s

Slide 64

Slide 64 text

JIT JIT JIT The Dark Side The Dark Side The Dark Side

Slide 65

Slide 65 text

Impact For The Zend Engine Developers

Slide 66

Slide 66 text

Maintenance xing bugs of something that generates assembly code

Slide 67

Slide 67 text

Stability JIT can introduce behavior differences

Slide 68

Slide 68 text

Platform Support Current JIT supports only x86 and x86_64 on Linux, MacOSX and Windows DynASM supports more CPUs, but work needed on Zend Engine side

Slide 69

Slide 69 text

JIT Impact For The PHP Developers

Slide 70

Slide 70 text

Potentially Different Bugs Depending On The JIT Con guration Pro ling con guration, triggering conditions, @jit tagged functions...

Slide 71

Slide 71 text

Debugger Support xdebug doesn't work (for now) with JIT enabled

Slide 72

Slide 72 text

Pro ler Support BlackFire will need to be adapted to work with JIT

Slide 73

Slide 73 text

Key Takeaways Don't expect the moon from JIT... ...but there's still a long way to go Test for your workload Look at PHP 7.4 preload for performances

Slide 74

Slide 74 text

Thank You! Questions? For more information: @bjacquemont wiki.php.net/rfc/jit