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

    View full-size slide

  2. What Happen To Our Code At
    Execution Time?
    $myVar = "World";
    echo "Hello ".$myVar."!\n";

    View full-size slide

  3. Lexing/Tokenizing
    Use the Tokenizer Extension
    $myVar = "World";
    echo "Hello ".$myVar."!\n";
    Line 1: T_OPEN_TAG ('')
    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 ('
    ')

    View full-size slide

  4. Parsing To Abstract Syntax Tree
    $myVar = "World";
    echo "Hello ".$myVar."!\n";

    View full-size slide

  5. Compilation: AST To Opcode
    Real value
    $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

    View full-size slide

  6. 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)

    View full-size slide

  7. Execution Of The Opcode
    By The Zend VM

    View full-size slide

  8. Opcodes Are The
    Instructions Of The VM
    Virtual Processor

    View full-size slide

  9. 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

    View full-size slide

  10. Why A VM?
    Reason #1
    Performances!

    View full-size slide

  11. Why A VM?
    Reason #2
    Abstraction Over The Architecture
    And Hardware

    View full-size slide

  12. Let's Dive Into
    Let's Dive Into
    Let's Dive Into
    Opcode!
    Opcode!
    Opcode!

    View full-size slide

  13. 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

    View full-size slide

  14. Hello World In Opcode!
    echo "Hello World !";
    $_main:
    L0: ECHO string("Hello World!")
    L1: RETURN int(1)

    View full-size slide

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

    View full-size slide

  16. 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)

    View full-size slide

  17. Control Structures

    View full-size slide

  18. 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)

    View full-size slide

  19. 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)

    View full-size slide

  20. 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)

    View full-size slide

  21. All Control Structures
    Can Be Implemented By
    JMP And IS_* Opcodes

    View full-size slide

  22. Why A VM?
    Reason #3
    Keep PHP expressiveness while really executing a
    simpler language

    View full-size slide

  23. 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

    View full-size slide

  24. Internal Functions
    echo microtime();
    $_main:
    L0: INIT_FCALL 0 string("microtime")
    L1: V0 = DO_ICALL
    L2: ECHO V0
    L3: RETURN int(1)

    View full-size slide

  25. 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)

    View full-size slide

  26. 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

    View full-size slide

  27. 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

    View full-size slide

  28. Opcode Cache: Monitoring
    $status = opcache_get_status();
    $status["memory_usage"]["used_memory"]
    $status["memory_usage"]["free_memory"]
    $status["opcache_statistics"]["num_cached_keys"]
    $status["opcache_statistics"]["max_cached_keys"]
    $status["opcache_statistics"]["opcache_hit_rate"]

    View full-size slide

  29. Opcode Cache: Memory Settings
    default 64 MB
    increase when free_memory~0
    opcache.memory_consumption

    View full-size slide

  30. 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

    View full-size slide

  31. The Opcode Optimizer
    Multipass Static Analysis To
    Optimize The Generated Opcode

    View full-size slide

  32. 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

    View full-size slide

  33. 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)

    View full-size slide

  34. 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)

    View full-size slide

  35. Optimizer - Performances?

    View full-size slide

  36. What's Next?
    What's Next?
    What's Next?

    View full-size slide

  37. PHP 8 Just In Time
    Compilation
    From opcodes, generating real CPU instructions to
    execute them directly on the hardware

    View full-size slide

  38. 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/

    View full-size slide

  39. 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

    View full-size slide

  40. 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)

    View full-size slide

  41. PHP 7.4 Preload
    How It Will Do It
    At server start, runs a PHP script that will load the
    needed le

    View full-size slide

  42. 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

    View full-size slide

  43. 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)

    View full-size slide

  44. 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

    View full-size slide

  45. Thank You!
    Questions?
    @bjacquemont
    www.akeneo.com

    View full-size slide