Slide 1

Slide 1 text

PHP: Under the Hood

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

Let’s start a conversation about mental health in tech mhprompt.org

Slide 4

Slide 4 text

How I Got Here

Slide 5

Slide 5 text

Profiling

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

The Performance Loop

Slide 8

Slide 8 text

Proprietary and Confidential The Performance Loop

Slide 9

Slide 9 text

When Should I Profile My Code?

Slide 10

Slide 10 text

Do I Have a Problem?

Slide 11

Slide 11 text

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?

Slide 12

Slide 12 text

Common Causes of Slowdowns

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

Agenda There is no Magic Bullet You’re In Trouble

Slide 15

Slide 15 text

Agenda Internals 101

Slide 16

Slide 16 text

Agenda Execution Life Cycle

Slide 17

Slide 17 text

Proprietary and Confidential Execution Life Cycle of PHP

Slide 18

Slide 18 text

Proprietary and Confidential Execution Lifecycle with an OpCode Cache

Slide 19

Slide 19 text

Proprietary and Confidential Execution Life Cycle of PHP

Slide 20

Slide 20 text

Tokenizing

Slide 21

Slide 21 text

Proprietary and Confidential The Worst “Hello World”, Ever sayHello("World"); ?>

Slide 22

Slide 22 text

Token Name Value T_OPEN_TAG T_STRING sayHello ( T_CONSTANT_ENCAPSED_STR ING "World" ) ; T_WHITESPACE T_CLOSE_TAG ?>

Slide 23

Slide 23 text

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"

Slide 24

Slide 24 text

Defining a String

Slide 25

Slide 25 text

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'

Slide 26

Slide 26 text

OpCodes

Slide 27

Slide 27 text

Agenda Vulcan Logic Dumper VLD

Slide 28

Slide 28 text

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 VLD - Vulcan Logic Dumper

Slide 29

Slide 29 text

Proprietary and Confidential • Outputs, in order: • global code (main script) • global functions • class functions VLD — Vulcan Logic Dumper

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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"; } }

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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");

Slide 36

Slide 36 text

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.

Slide 37

Slide 37 text

Variables

Slide 38

Slide 38 text

Agenda Zend Value zval

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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;

Slide 42

Slide 42 text

Strongly Typed

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

References & Copy-on-Write

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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)

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

More OpCodes

Slide 50

Slide 50 text

Agenda More than one way to do it… Incrementing By One

Slide 51

Slide 51 text

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++;

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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;

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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;

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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;

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

Agenda Quoting Strings

Slide 60

Slide 60 text

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';

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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";

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

Agenda Concatenation

Slide 66

Slide 66 text

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";

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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";

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

Function/Method Calls

Slide 71

Slide 71 text

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()

Slide 72

Slide 72 text

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)

Slide 73

Slide 73 text

Proprietary and Confidential • SEND_VAL: Queue up two arguments to send to the function • DO_FCALL: Call the function Function Call with Arguments

Slide 74

Slide 74 text

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);

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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();

Slide 78

Slide 78 text

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)

Slide 79

Slide 79 text

Proprietary and Confidential Feedback & Questions: 
 Twitter: @dshafik Email: [email protected] Slides: http://daveyshafik.com/slides