Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHP OpCode? What's that?

PHP OpCode? What's that?

Every PHP developer heard about OpCode, the magical sauce that runs inside the Zend PHP Engine in order to boost performances.

But what is exactly that OpCode? Why does it make the Zend Engine faster to run your PHP Code? Is it only about performances?

The aim of this talk is to demystify what's going on inside the Zend PHP Engine and to understand what is happening to our PHP code when it's digested by the interpreter and turned into opcode, before being executed on the Zend Virtual Machine.

Benoit Jacquemont

June 08, 2019
Tweet

More Decks by Benoit Jacquemont

Other Decks in Programming

Transcript

  1. PHP OpCode PHP OpCode PHP OpCode What's That? What's That?

    What's That? Benoit Jacquemont Benoit Jacquemont Benoit Jacquemont @bjacquemont @bjacquemont @bjacquemont
  2. What Happen To Our Code At Execution Time? <?php $myVar

    = "World"; echo "Hello ".$myVar."!\n";
  3. Lexing/Tokenizing Use the Tokenizer Extension <?php $myVar = "World"; echo

    "Hello ".$myVar."!\n"; Line 1: T_OPEN_TAG ('<?php ') Line 2: T_WHITESPACE (' ') Line 2: T_VARIABLE ('$myVar') Line 2: T_WHITESPACE (' ') string(1) "=" Line 2: T_WHITESPACE (' ') Line 2: T_CONSTANT_ENCAPSED_STRING ('"World"') string(1) ";" Line 2: T_WHITESPACE (' ') Line 3: T_ECHO ('echo') Line 3: T_WHITESPACE (' ') Line 3: T_CONSTANT_ENCAPSED_STRING ('"Hello "') string(1) "." Line 3: T_VARIABLE ('$myVar') string(1) "." Line 3: T_CONSTANT_ENCAPSED_STRING ('"!\n"') string(1) ";" Line 3: T_WHITESPACE (' ')
  4. Compilation: AST To Opcode Real value <?php $myVar = "World";

    echo "Hello ".$myVar."!\n"; L0 (2): ASSIGN CV0($myVar) string("World") L1 (3): T2 = CONCAT string("Hello ") CV0($myVar) L2 (3): T3 = CONCAT T2 string("! ") L3 (3): ECHO T3 L4 (4): RETURN int(1) 0x26 CV0 0x2b46aa8 T2 = 0x08 0x2b49250 CV0 T3 = 0x08 T2 0x2b489f0 0x28 T3 0x3E 0x01
  5. Opcode, Opline And Oparray opcode: instruction to execute opline: instruction

    with operands (0-2) and optional return assignement oparray: sequence of oplines (function, closure, main) CONCAT (0x08) T2 = CONCAT string("Hello ") CV0 ASSIGN CV0 string("World") T2 = CONCAT string("Hello ") CV0 ECHO T2 RETURN int(1)
  6. PHP Version Zend Engine Version Opcodes count 4.x 1 ~130

    5.x 2 167 7.x 3 172 8.x 4 207 Source: Zend/zend_vm_opcodes.h
  7. How To Display Opcode VLD Extension (from Derick Rethans) (https://pecl.php.net/package/vld)

    php opcache debug (since PHP 7.1) $ php -dvld.active=1 test.php $ php -d opcache.opt_debug_level=0x10000 test.php
  8. Hello World In Opcode! echo "Hello World !"; $_main: L0:

    ECHO string("Hello World!") L1: RETURN int(1)
  9. What About Variables? $msg = "Hello World!"; echo $msg; $_main:

    L0: ASSIGN CV0($msg) string("Hello World!") L1: ECHO CV0($msg) L2: RETURN int(1)
  10. More Complex Variables $a = 1; $b = 2; $result

    = $a + $b + 3; echo $result; $_main: L0: ASSIGN CV0($a) int(1) L1: ASSIGN CV1($b) int(2) L2: T5 = ADD CV0($a) CV1($b) L3: T6 = ADD T5 int(3) L4: ASSIGN CV2($result) T6 L5: ECHO CV2($result) L6: RETURN int(1)
  11. If/Else $test = true; if ($test) { echo "Hello World!";

    } else { echo "Bye World!"; } $_main: L0: ASSIGN CV0($test) bool(true) L1: JMPZ CV0($test) L4 L2: ECHO string("Hello World!") L3: JMP L5 L4: ECHO string("Bye World!") L5: RETURN int(1)
  12. While $test = true; while ($test) { echo "Hello World!";

    $test = false; } $_main: L0: ASSIGN CV0($test) bool(true) L1: JMP L4 L2: ECHO string("Hello World!") L3: ASSIGN CV0($test) bool(false) L4: JMPNZ CV0($test) L2 L5: RETURN int(1)
  13. For for ($i = 0; $i < 10; $i++) {

    echo "Hello World!"; } $_main: L0: ASSIGN CV0($i) int(0) L1: JMP L5 L2: ECHO string("Hello World!") L3: T2 = POST_INC CV0($i) L4: FREE T2 L5: T3 = IS_SMALLER CV0($i) int(10) L6: JMPNZ T3 L2 L7: RETURN int(1)
  14. A Hypothetical Control Structure: Forever New Language Feature, But No

    Need To Change The VM forever { echo "Hello World!"; } $_main: L0: ECHO string("Hello World!") L1: JMP L0
  15. Internal Functions With Parameters $subject = "Such a nice message!";

    str_replace("nice", "cool", $subject); $_main: L0: ASSIGN CV0($subject) string("Such a nice message!") L1: INIT_FCALL 3 string("str_replace") L2: SEND_VAL string("nice") 1 L3: SEND_VAL string("cool") 2 L4: SEND_VAR CV0($subject) 3 L5: DO_ICALL L6: RETURN int(1)
  16. User Function function hello($name) { echo "Hello $name!"; } hello("World");

    $_main: L0: NOP L1: INIT_FCALL 1 string("hello") L2: SEND_VAL string("World") 1 L3: DO_UCALL L4: RETURN int(1) hello: L0: CV0($name) = RECV 1 L1: T1 = CONCAT string("Hello ") CV0($name) L2: T2 = CONCAT T1 string("!") L3: ECHO T2 L4: RETURN null
  17. Inheritance abstract class MyParentClass { function helloWorld() { echo "Hello

    World"; } } class MyChildClass extends MyParentClass{} $object = new MyChildClass(); $object->helloWorld(); $_main: L0: V4 = NEW 0 string("MyChildClass") L1: DO_FCALL L2: ASSIGN CV0($object) V4 L3: INIT_METHOD_CALL 0 CV0($object) string("helloWorld") L4: DO_FCALL L5: RETURN int(1) MyParentClass::helloWorld L0: ECHO string("Hello World") L1: RETURN null
  18. Opcode Cache: Num Keys Settings default 2000 inc when num_cached_keys~max_cached_keys

    The actual value used will be the rst number in the set of prime numbers {463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987} that is greater than or equal to the con gured value. opcache.max_accelerated_files
  19. Optimizer Before And After Opcodes Before optimizer After optimizer $

    php -d opcache.opt_debug_level=0x10000 test.php $ php -d opcache.opt_debug_level=0x20000 test.php
  20. Optimizer - Before And After Before After if ("foo") {

    echo "Foo!"; } else { echo "Not Foo!"; } L0: JMPZ string("foo") L3 L1: ECHO string("Foo!") L2: JMP L4 L3: ECHO string("Not Foo!") L4: RETURN int(1) L0: ECHO string("Foo!") L1: RETURN int(1)
  21. Optimizer - Before And After Before After for ($i =

    0; $i < 10; $i++) { echo "Hello World!"; } L0: ASSIGN CV0($i) int(0) L1: JMP L5 L2: ECHO string("Hello World!") L3: T2 = POST_INC CV0($i) L4: FREE T2 L5: T3 = IS_SMALLER CV0($i) 10 L6: JMPNZ T3 L2 L7: RETURN int(1) L0: ASSIGN CV0($i) int(0) L1: JMP L4 L2: ECHO string("Hello World!") L3: PRE_INC CV0($i) L4: T1 = IS_SMALLER CV0($i) 10 L5: JMPNZ T1 L2 L6: RETURN int(1)
  22. PHP 8 Just In Time Compilation From opcodes, generating real

    CPU instructions to execute them directly on the hardware
  23. PHP 8 JIT - Current State 5x speedup on number

    crunching bench.php, but... ... -0.01% performance on real world applications Main idea: allow PHP to enter areas where it's not used yet github.com/zendtech/php-src/tree/jit-dynasm/
  24. PHP 7.4 Preload The Main Opcache Limitation Opcache caches opcode

    but at le level classes and functions still need to be reloaded into the process memory
  25. PHP 7.4 Preload What It Will Do Permanently load in

    the server memory functions and classes Once loaded, these classes will be available directly, like builtins (\Exception)
  26. PHP 7.4 Preload How It Will Do It At server

    start, runs a PHP script that will load the needed le
  27. PHP 7.4 Preload What Can We Expect From It ZF2

    Hello world app: 2x requests per second If application fully loaded, no need for autoload anymore
  28. Key Takeaways opcodes are the instructions of the Zend virtual

    CPU use opcode cache! monitor your opcode cache check out preload for PHP 7.4 (ETA end of the year)
  29. For More Information Nikita Popov's Blog (nikic.github.io) Julien Pauli's Blog

    (blog.jpauli.tech) Preload RFC PHP 7 Virtual Machine PHP's OPCache extension wiki.php.net/rfc/preload