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

[phpDay 2014] PHP: Under The Hood

[phpDay 2014] PHP: Under The Hood

We’ve all experienced performance issues and we would typically turn to a profiler. Whether that’s something in userland, or a tool like xdebug or xhprof, the reason is the same: to figure out why our code is slow.

This talk will take that inspection a step further and look under the hood of PHP, at the C internals, for common performance problems.

If you’ve ever wanted to know exactly what your code is doing, and why ++$i is faster than $i++, this talk is for you.

Davey Shafik

May 16, 2014

Transcript

83 slides
  1. Proprietary and Confidential •Community Engineer at Engine Yard •Author of

    Zend PHP 5 Certification Study Guide, Sitepoints PHP Anthology: 101 Essential Tips, Tricks & Hacks & PHP Master: Write Cutting Edge Code •A contributor to Zend Framework 1 & 2, phpdoc, and PHP internals • Original creator of PHAR/ PHP_Archive • Lead for PHPWomen US •@dshafik Davey Shafik
  2. Proprietary and Confidential • Profiling Records Relative Speed + Memory

    Usage + # of calls per function + Call graph! • The act of profiling affects the speed (the outcome) of the code (just like quantum physics!)! • Benchmarking Tests Actual Speed! • What your users actually see Benchmarking Vs Profiling
  3. Proprietary and Confidential • The #1 issue with performance tuning,

    is assuming you even have a problem.! • Premature Optimization is a waste of time.! • Determine desired performance (e.g. 100 concurrent users with less than 1s response times). Benchmark on production hardware. Do you even have a problem? How big? Do I Have a Problem?
  4. Proprietary and Confidential • Datastore! • Doesn’t matter if it’s

    PostgreSQL, MySQL, Oracle, MongoDB, CouchDB, or MSSQL! • External Resources! • APIs, Filesystems, Network Sockets, External Processes! • Bad Code! • The only great code, is code that never has to run. Everything else, is just good code. Common Causes of Slowdowns
  5. Proprietary and Confidential The Worst “Hello World”, Ever <?php class

    Greeting { public function sayHello($to) { echo "Hello $to"; } } ! $greeter = new Greeting(); $greeter->sayHello("World"); ?>
  6. Token Name Value T_OPEN_TAG <?php T_CLASS class T_WHITESPACE T_STRING Greeting

    T_WHITESPACE { T_WHITESPACE T_PUBLIC public T_WHITESPACE T_FUNCTION function T_WHITESPACE T_STRING sayHello ( T_VARIABLE $to ) T_WHITESPACE { T_WHITESPACE T_ECHO echo T_WHITESPACE " T_ENCAPSED_AND_WHITESP Hello T_VARIABLE $to " ; Token Name Value T_WHITESPACE } T_WHITESPACE } T_WHITESPACE T_VARIABLE $greeter T_WHITESPACE = T_WHITESPACE T_NEW new T_WHITESPACE T_STRING Greeting ( ) ; T_WHITESPACE T_VARIABLE $greeter T_OBJECT_OPERATOR -> T_STRING sayHello ( T_CONSTANT_ENCAPSED_STR ) ; T_WHITESPACE T_CLOSE_TAG ?>
  7. Proprietary and Confidential • Note the difference between interpolated and

    non-interpolated strings.! • They are both double quoted strings!! • Interpolated: 4 Tokens! ! ! • Non-interpolated: 1 Token! ! ! • Start to see why this matters for performance: but don’t get caught up by micro-optimizations. Tokenization " T_ENCAPSED_AND_WHIT Hello T_VARIABLE $to " T_CONSTANT_ENCAPSED "World"
  8. echo "bar"; T_ECHO echo T_CONSTANT_ENCAPSED_STRING "bar" T_ECHO echo " T_VARIABLE

    $a T_ENCAPSED_AND_WHITESPACE bar " T_ECHO echo " T_CURLY_OPEN { T_VARIABLE $a } T_ENCAPSED_AND_WHITESPACE bar " echo 'bar'; echo "$a bar"; echo "{$a} bar"; T_ECHO echo T_CONSTANT_ENCAPSED_STRING 'bar'
  9. Proprietary and Confidential • An extension to dump the compiled

    opcodes! ! • Installed via PECL
 $ pecl install vld-beta! ! • Add the following to php.ini:
 extension=vld.so! ! • Use via command line:
 
 $ php -dvld.active=1 -dvld.execute=0 <file> VLD - Vulcan Logic Dumper
  10. Proprietary and Confidential • Outputs, in order:! • global code

    (main script)! • global functions! • class functions VLD — Vulcan Logic Dumper
  11. Proprietary and Confidential Header Understanding VLD Dumps Class Greeting: Function

    sayhello: filename: ./Greeting.php function name: sayHello
  12. Proprietary and Confidential Header Understanding VLD Dumps Class Greeting: Function

    sayhello: filename: ./Greeting.php function name: sayHello number of ops: 8
  13. Proprietary and Confidential Header Understanding VLD Dumps Class Greeting: Function

    sayhello: filename: ./Greeting.php function name: sayHello compiled vars: !0 = $to number of ops: 8
  14. Proprietary and Confidential Understanding VLD Output (Cont.) • line: The

    line number in the source file! • #: The opcode number • *: entry (left aligned) and exit points (right aligned), indicated by greater than symbols (>) • op: The opcode name! • fetch: Details on global variable fetches (super globals, or the use of the global keyword)! • ext: Extra data associated with the opcode, for example the opcode to which it should JMP! • return: The location where return data from the operation is stored • operands: the operands used by the opcode (e.g. two variables to concat) line # * op fetch ext return operands ----------------------------------------------------------------
  15. Proprietary and Confidential VLD — Variables • Four types of

    variables! Exclamation point (!) are compiled variables (CVs) — these are pointers to userland variables! Tilde (~) are temporary variables used for temporary storage (TMP_VARs) of in-process operations Dollar ($) are another type of temporary variables (VARs) which are tied to userland variables like CVs and therefore require things like refcounting.! Colon (:) are temporary variables used for the storage of the result of lookups in the class hashtable
  16. Proprietary and Confidential class Greeting -> sayHello Class Greeting: Function

    sayhello: number of ops: 5 compiled vars: !0 = $to line # * op fetch ext return operands --------------------------------------------------------------- 3 0 > RECV !0 4 1 ADD_STRING ~0 'Hello+' 2 ADD_VAR ~0 ~0, !0 3 ECHO ~0 5 4 > RETURN null class Greeting { public function sayHello($to) { echo "Hello $to"; } }
  17. Proprietary and Confidential class Greeting -> sayHello • RECV: The

    function receives a value which is assigned to !0 (which represents $to) • ADD_STRING: Next we create a temporary variable identified by a ~, ~0 and assign the static string,’Hello+', where the + represents a space! • ADD_VAR: After this, we concat the contents our variable, !0 to our temporary variable, ~0 • ECHO: Then we echo that temporary variable! • RETURN: Finally we return null as the function ends
  18. Proprietary and Confidential Main Script number of ops: 9 compiled

    vars: !0 = $greeter line # * op fetch ext return operands ------------------------------------------------------------------ 2 0 > NOP 8 1 FETCH_CLASS 4 :1 'Greeting' 2 NEW $2 :1 3 DO_FCALL_BY_NAME 0 4 ASSIGN !0, $2 9 5 INIT_METHOD_CALL !0, 'sayHello' 6 SEND_VAL 'World' 7 DO_FCALL_BY_NAME 1 10 8 > RETURN 1 $greeter = new Greeting(); $greeter->sayHello("World");
  19. Proprietary and Confidential Main Script • FETCH_CLASS: First we lookup

    the class, Greeting; we store this reference in :1! • NEW: Then we instantiate an instance of the class (:1) and assign it to a VAR, $2, and! • DO_FCALL_BY_NAME: call the constructor! • ASSIGN: Next we assign the resulting object (in VAR $2) to our CV (!0)! • INIT_METHOD_CALL: We start calling the sayHello method, and! • SEND_VAL: pass in 'World' to the method, and! • DO_FCALL_BY_NAME: Actually execute the method! • RETURN: The script ends successfully with an implicit return of 1.
  20. Proprietary and Confidential • C data structure! • Used to

    store all PHP variable ($foo) data! • Probably the most important thing in PHP! ! typedef struct _zval_struct { zvalue_value value; zend_uint refcount__gc; zend_uchar type; zend_uchar is_ref__gc; } zval; zval
  21. Proprietary and Confidential zval member name value Stores the actual

    data refcount__gc Number of references to the zval type Stores the data type is_ref__gc Whether the variable is a reference
  22. Proprietary and Confidential • C Union! • Stores only one

    piece of data! • Memory is as large as it’s largest member! • Stores the actual data! • Strongly Typed zval value typedef union _zvalue_value { long lval; double dval; struct { char *val; int len; } str; HashTable *ht; zend_object_value obj; } zvalue_value;
  23. Proprietary and Confidential • At the C-level, values are strongly

    typed! • PHP coerces the data automatically for things like comparisons! • PHP never changes the zvals actual type Strongly Typed Data Type ZVAL type Value Storage Location NULL IS_NULL none Integer IS_LONG lval Float IS_DOUBLE dval String IS_STRING str.*val, str.len Array IS_ARRAY *ht Object IS_OBJECT obj Boolean IS_BOOL lval Resouce IS_RESOURCE lval
  24. Proprietary and Confidential • To avoid copying values, PHP utilizes

    reference counting.! • When one variable is assigned to another, it is always as a reference until the data is changed.! • This is different from PHP references using &! • Easier to show with examples References & Copy-on-Write
  25. Proprietary and Confidential Refcounting $a = 1; // $a =

    zval#1(value=1, refcount=1) $b = $a; // $a = $b = zval#1(value=1, refcount=2) $c = $b; // $a = $b = $c = zval#1(value=1, refcount=3) $a++; // $b = $c = zval#1(value=1, refcount=2) // $a = zval#2(value=2, refcount=1) unset($b); // $c = zval#1(value=1, refcount=1) // $a = zval#2(value=2, refcount=1) unset($c); // zval#1 is destroyed, because refcount=0 // $a = zval#2(value=2, refcount=1)
  26. Proprietary and Confidential Refcounting & PHP References $a = 1;

    // $a = zval#1(value=1, refcount=1, is_ref=0) $b =& $a; // $a = $b = zval#1(value=1, refcount=2, is_ref=1) $b++; // $a = $b = zval#1(value=2, refcount=2, is_ref=1) // Due to the is_ref=1 PHP directly changes the zval // rather than making a copy
  27. Proprietary and Confidential Refcounting & PHP References $a = 1;

    // $a = zval#1(value=1, refcount=1, is_ref=0) $b = $a; // $a = $b = zval#1(value=1, refcount=2, is_ref=0) $c = $b // $a = $b = $c = zval#1(value=1, refcount=3, is_ref=0) $d =& $c; // $a = $b = zval#1(value=1, refcount=2, is_ref=0) // $c = $d = zval#2(value=1, refcount=2, is_ref=1) // $d is a reference of $c, but *not* of $a and $b, so // the zval needs to be copied here. Now we have the // same zval once with is_ref=0 and once with is_ref=1. $d++; // $a = $b = zval#1(value=1, refcount=2, is_ref=0) // $c = $d = zval#2(value=2, refcount=2, is_ref=1) // Because there are two separate zvals $d++ does // not modify $a and $b (as expected).
  28. Proprietary and Confidential Incrementing By One: Post-Increment number of ops:

    4 compiled vars: !0 = $i line # * op fetch ext return operands ------------------------------------------------------------------ 2 0 > ASSIGN !0, 1 4 1 POST_INC ~1 !0 2 FREE ~1 3 > RETURN 1 $i = 1; $i++;
  29. Proprietary and Confidential • ASSIGN: We assign the value (1)

    to the CV !0 • POST_INC: We increment the CV !0 and store it in the temporary variable, ~1.! • FREE: Free the temporary variable (~1) Incrementing By One: Post Increment
  30. Proprietary and Confidential Incrementing By One: Pre-Increment number of ops:

    5 compiled vars: !0 = $i line # * op fetch ext return operands ----------------------------------------------------------------- 2 0 > ASSIGN !0, 1 4 1 PRE_INC !0 5 2 > RETURN 1 $i = 1; ++$i;
  31. Proprietary and Confidential • ASSIGN: We assign the value (1)

    to the CV !0! • PRE_INC: We increment the CV !0 Incrementing By One: Pre-Increment
  32. Proprietary and Confidential Incrementing By One: Re-assignment number of ops:

    6 compiled vars: !0 = $i line # * op fetch ext return operands ------------------------------------------------------------------ 2 0 > ASSIGN !0, 1 4 1 ADD ~1 !0, 1 2 ASSIGN !0, ~1 5 3 > RETURN 1 $i = 1; $i = $i + 1;
  33. Proprietary and Confidential • ASSIGN: We assign the value (1)

    to the CV !0 • ADD: We add the CV (!0) to 1 and assign it to a temporary variable ~1! • ASSIGN: We assign the temporary variable ~1 to the CV (!0) Incrementing By One: Re-Assignment
  34. Proprietary and Confidential Incrementing By One: Assign-Add number of ops:

    5 compiled vars: !0 = $i line # * op fetch ext return operands ------------------------------------------------------------------ 2 0 > ASSIGN !0, 1 4 1 ASSIGN_ADD 0 !0, 1 5 2 > RETURN 1 $i#=#1;# ## $i#+=#1;
  35. Proprietary and Confidential • ASSIGN: We assign the value (1)

    to the CV !0 • ASSIGN_ADD: We add 1 to the CV (!0) Incrementing By One: Assign-Add
  36. Proprietary and Confidential Quoting Strings: Single Quotes number of ops:

    2 compiled vars: none line # * op etch ext return operands ---------------------------------------------------------- 2 0 ECHO 'bar' 3 1 > RETURN 1 echo#'bar';
  37. Proprietary and Confidential Quoting Strings: Double Quotes echo#"bar"; number of

    ops: 2 compiled vars: none line # * op etch ext return operands ---------------------------------------------------------- 2 0 ECHO 'bar' 3 1 > RETURN 1
  38. Proprietary and Confidential Quoting Strings: Simple Interpolation number of ops:

    5 compiled vars: !0 = $a line # * op fetch ext return operands ------------------------------------------------------------------ 2 0 ASSIGN !0, 'foo' 3 1 ADD_VAR ~1 !0 2 ADD_STRING ~1 ~1, '+bar' 3 ECHO ~1 4 4 > RETURN 1 $a = 'foo'; echo "$a bar";
  39. Proprietary and Confidential • ASSIGN: We assign the value ('foo')

    to the CV !0! • ADD_VAR: We create a temporary variable (~1) to hold our interpolate string, and assign the CV !0 to it! • ADD_STRING: We append the string '+bar' to the temporary variable ~1 and assign it back to ~1! • ECHO: We output the string in our temporary variable ~1 Quoting Strings: Simple Interpolation
  40. Proprietary and Confidential Quoting Strings: Complex Interpolation $a = 'foo';

    echo "{$a} bar"; number of ops: 5 compiled vars: !0 = $a line # * op fetch ext return operands ------------------------------------------------------------------ 2 0 ASSIGN !0, 'foo' 3 1 ADD_VAR ~1 !0 2 ADD_STRING ~1 ~1, '+bar' 3 ECHO ~1 4 4 > RETURN 1
  41. Proprietary and Confidential number of ops: 4 compiled vars: !0

    = $a line # * op fetch ext return operands ------------------------------------------------------------------ 2 0 > ASSIGN !0, 'foo' 4 1 CONCAT ~1 !0, '+bar' 2 ASSIGN !0, ~1 5 3 > RETURN 1 Simple Concatenation $a = "foo"; $a = $a . " bar";
  42. Proprietary and Confidential • ASSIGN: We assign the value ('foo')

    to the CV !0 • CONCAT: We concatenate '+bar' to the compiled variable !0 and assign it to a temporary variable, ~1! • ASSIGN: We assign the complete string back to the compiled variable !0 Simple Concatenation
  43. Proprietary and Confidential number of ops: 5 compiled vars: !0

    = $a line # * op fetch ext return operands ------------------------------------------------------------------ 2 0 > ASSIGN !0, 'foo' 1 ASSIGN_CONCAT 0 !0, '+bar' 5 2 > RETURN 1 Assign-Concat $a = "foo"; $a .= " bar";
  44. Proprietary and Confidential • ASSIGN: We assign the value ('foo')

    to the CV !0 • ASSIGN_CONCAT: We concat the value "+bar" to the CV !0 Assign-Concat
  45. Proprietary and Confidential number of ops: 2 compiled vars: none

    line # * op fetch ext return operands ------------------------------------------------------------------ 2 0 > DO_FCALL 0 'phpinfo' 3 1 > RETURN 1 Simple Function Call phpinfo()
  46. Proprietary and Confidential number of ops: 4 compiled vars: none

    line # * op fetch ext return operands ------------------------------------------------------------------ 2 0 > SEND_VAL 1 1 SEND_VAL 3 2 DO_FCALL 2 'bcadd' 3 3 > RETURN 1 Function Call with Arguments bcadd(1, 3)
  47. Proprietary and Confidential • SEND_VAL: Queue up two arguments to

    send to the function! • DO_FCALL: Call the function Function Call with Arguments
  48. Proprietary and Confidential number of ops: 5 compiled vars: none

    line # * op fetch ext return operands ------------------------------------------------------------------ 2 0 > NOP 7 1 SEND_VAL 1 2 SEND_VAL 3 3 DO_FCALL 2 'add' 8 4 > RETURN 1 Userland Function function add($a, $b) { $c = $a + $b; return $c; } add(1, 3);
  49. Proprietary and Confidential Function add: number of ops: 6 compiled

    vars: !0 = $a, !1 = $b, !2 = $c line # * op fetch ext return operands --------------------------------------------------------------- 2 0 > RECV !0 1 RECV !1 3 2 ADD ~0 !0, !1 3 ASSIGN !2, ~0 4 4 > RETURN !2 5 5* > RETURN null Userland Function (cont.)
  50. Proprietary and Confidential • RECV: Receive two arguments, assign them

    to compiled variables !0, !1! • ADD: Add the two variables, and assign to a temporary variable, ~0! • ASSIGN: Assign the result to a third compiled variables, !2! • RETURN: Return the third compiled variable Userland Function (cont.)
  51. Proprietary and Confidential line # * op fetch ext return

    operands -------------------------------------------------------------------- 2 0 > NOP 7 1 FETCH_CLASS 4 :1 'foo' 2 NEW $2 :1 3 DO_FCALL_BY_NAME 0 4 ASSIGN !0, $2 8 5 INIT_METHOD_CALL !0, 'bar' 6 DO_FCALL_BY_NAME 0 9 7 INIT_METHOD_CALL !0, 'bat' 8 DO_FCALL_BY_NAME 0 10 9 > RETURN 1 Method Overloading (__call) class foo { public function bar() {} public function __call($a, $b) {} } $foo = new foo; $foo->bar(); $foo->bat();
  52. Proprietary and Confidential • FETCH_CLASS: We do a class lookup

    and store the value in a temporary variable, :1! • NEW: We instantiate the class, assigning the object to temporary variable $2 (which requires refcounting)! • DO_FCALL_BY_NAME: We call the constructor! • ASSIGN: We assign the instantiated object in $2, to CV !0! • INIT_METHOD_CALL: We start calling the method! • DO_FCALL_BY_NAME: We call the 'bar' method! • INIT_METHOD_CALL: We start calling the method! • DO_FCALL_BY_NAME: We call the 'bat' method Method Overloading (__call)

More Decks by Davey Shafik

Other Decks in Programming