[PHPNW14] PHP: Under The Hood

[PHPNW14] PHP: Under The Hood

Performance issues can be caused by many things, from database interactions, web services, disk i/o and, less frequently, the code itself. We would typically turn to a profiler like xhprof to diagnose these issues, but what if the bottleneck is PHP itself, where do you turn?

This talk will take that inspection a step further and look under the hood of PHP, at the C internals of how things tick. This talk covers what every PHP developer should know about their tools — like what's really going on when you use double quotes vs single quotes.

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.

Fee39f0c0ffb29d9ac21607ed188be6b?s=128

Davey Shafik

October 05, 2014
Tweet

Transcript

  1. PHP: Under the Hood

  2. 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, & PHP internals • Original creator of PHAR/ PHP_Archive • Lead for PHPWomen US •@dshafik Davey Shafik
  3. $30 (Includes Taxes & Shipping)

  4. Let’s start a conversation about mental health in tech prompt.engineyard.com

  5. How I Got Here

  6. Profiling

  7. 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
  8. The Performance Loop

  9. Proprietary and Confidential The Performance Loop

  10. When Should I Profile My Code?

  11. Do I Have a Problem?

  12. 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?
  13. Common Causes of Slowdowns

  14. 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
  15. Agenda There is no Magic Bullet You’re In Trouble

  16. Agenda Internals 101

  17. Agenda Execution Life Cycle

  18. Proprietary and Confidential Execution Life Cycle of PHP

  19. Proprietary and Confidential Execution Life Cycle of PHP

  20. Proprietary and Confidential Execution Lifecycle with an OpCode Cache

  21. Proprietary and Confidential Execution Life Cycle of PHP

  22. Proprietary and Confidential Execution Life Cycle of PHP

  23. Tokenizing

  24. Proprietary and Confidential The Worst “Hello World”, Ever <?php class

    Greeting { public function sayHello($to) { echo "Hello $to"; } } ! $greeter = new Greeting(); $greeter->sayHello("World"); ?>
  25. 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 ?>
  26. 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_WHITESPACE Hello T_VARIABLE $to " T_CONSTANT_ENCAPSED _STRING "World"
  27. Defining a String

  28. 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'
  29. OpCodes

  30. Agenda Vulcan Logic Dumper VLD

  31. 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
  32. Proprietary and Confidential • Outputs, in order:! • global code

    (main script)! • global functions! • class functions VLD — Vulcan Logic Dumper
  33. Proprietary and Confidential Header Understanding VLD Dumps

  34. Proprietary and Confidential Header Understanding VLD Dumps Class Greeting: Function

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

    sayhello: filename: ./Greeting.php function name: sayHello number of ops: 8
  36. 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
  37. 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 ----------------------------------------------------------------
  38. 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
  39. 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"; } }
  40. 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
  41. 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");
  42. 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.
  43. Variables

  44. Agenda Zend Value zval

  45. 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
  46. 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
  47. 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;
  48. Strongly Typed

  49. 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 Resource IS_RESOURCE lval
  50. References & Copy-on-Write

  51. 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
  52. 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)
  53. 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
  54. 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).
  55. More OpCodes

  56. Agenda More than one way to do it… Incrementing By

    One
  57. 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++;
  58. 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
  59. 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;
  60. 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
  61. 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;
  62. 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
  63. 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;
  64. 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
  65. Agenda Quoting Strings

  66. 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';
  67. 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
  68. 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";
  69. 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
  70. 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
  71. Agenda Concatenation

  72. 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";
  73. 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
  74. 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";
  75. 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
  76. Function/Method Calls

  77. 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()
  78. 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)
  79. Proprietary and Confidential • SEND_VAL: Queue up two arguments to

    send to the function! • DO_FCALL: Call the function Function Call with Arguments
  80. 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);
  81. 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.)
  82. 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.)
  83. 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();
  84. 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)
  85. Proprietary and Confidential Feedback & Questions: ! Feedback: https://joind.in/
 Twitter:

    @dshafik Email: davey@engineyard.com Slides: http://daveyshafik.com/slides 11799