Slide 1

Slide 1 text

PHP Performance Under The Hood (or: Here be dragons!)

Slide 2

Slide 2 text

•Community Engineer @ 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, phpdoc, FRAPI, PHP internals •Original creator of PHP Archives (PHAR) • @dshafik Davey Shafik

Slide 3

Slide 3 text

About These Slides

Slide 4

Slide 4 text

About These Slides • Two slides per “slide” • Title Slide (for when I’m talking) • Details slide (for later) • Nobody likes it when you can read the slide just as well as the speaker can • I like slides that are useful !4

Slide 5

Slide 5 text

What is Profiling?

Slide 6

Slide 6 text

Benchmarking Vs Profiling • 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

Slide 7

Slide 7 text

BULLET • Bullet point • Bullet point • Bullet point BULLET • Bullet point • Bullet point • Bullet point BULLET • Bullet point • Bullet point • Bullet point BULLET • Bullet point • Bullet point • Bullet point BULLET • Bullet point • Bullet point • Bullet point BULLET • Bullet point • Bullet point • Bullet point BULLET • Bullet point • Bullet point • Bullet point BULLET • Bullet point • Bullet point • Bullet point The Performance Loop

Slide 8

Slide 8 text

Benchmark Profile Make Changes

Slide 9

Slide 9 text

When Should I Profile My Code?

Slide 10

Slide 10 text

Do You Have a Problem? • 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?

Slide 11

Slide 11 text

Common Causes of Slowdowns

Slide 12

Slide 12 text

Common Causes of Slowdown 1. Datastore • Doesn’t matter if it’s PostgreSQL, MySQL, Oracle, MongoDB, CouchDB, or MSSQL 2. External Resources • APIs, Filesystems, Network Sockets, External Processes 3. Bad Code • The only great code, is code that never has to run. Everything else, is just good code.

Slide 13

Slide 13 text

You’re In Trouble No magic bullet

Slide 14

Slide 14 text

Internals 101

Slide 15

Slide 15 text

Execution Life-Cycle

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

Execution Lifecycle with an OpCode Cache

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

Tokenizing

Slide 20

Slide 20 text

The Worst Hello World, Ever sayHello("World"); ?>

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

Tokenizing • 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. !22 " T_ENCAPSED_AND_WHITESPACE Hello T_VARIABLE $to " T_CONSTANT_ENCAPSED_STRING "World"

Slide 23

Slide 23 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 24

Slide 24 text

OpCodes

Slide 25

Slide 25 text

VLD Vulcan Logic Dumper

Slide 26

Slide 26 text

VLD — Vulcan Logic Dumper • 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 !26

Slide 27

Slide 27 text

VLD — Vulcan Logic Dumper • Outputs, in order: • global code (main script) • global functions • class functions !27

Slide 28

Slide 28 text

Understanding VLD Dumps Class Greeting: Function sayhello: filename: ./Greeting.php function name: sayHello Header compiled vars: !0 = $to number of ops: 8

Slide 29

Slide 29 text

Understanding VLD Dumps (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) !29 line # * op fetch ext return operands ----------------------------------------------------------------

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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 32

Slide 32 text

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

Slide 33

Slide 33 text

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 34

Slide 34 text

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

Slide 35

Slide 35 text

Variables

Slide 36

Slide 36 text

zval Zend Value

Slide 37

Slide 37 text

zval • 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; !37

Slide 38

Slide 38 text

zval !38 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 39

Slide 39 text

zval value • C Union • Stores the actual data • Strongly Typed !39 typedef union _zvalue_value { long lval; double dval; struct { char *val; int len; } str; HashTable *ht; zend_object_value obj; } zvalue_value;

Slide 40

Slide 40 text

Strongly Typed

Slide 41

Slide 41 text

Strongly Typed • At the C-level, values are strongly typed • PHP coerces the data automatically for things like comparisons • PHP never changes the zvals actual type !41 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

Slide 42

Slide 42 text

References & Copy-on-Write

Slide 43

Slide 43 text

References & Copy-on-Write • 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 !43

Slide 44

Slide 44 text

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 45

Slide 45 text

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 46

Slide 46 text

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 47

Slide 47 text

Optimizing OpCodes

Slide 48

Slide 48 text

Incrementing By One More than one way to do it…

Slide 49

Slide 49 text

Incrementing By One 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 50

Slide 50 text

Incrementing By One • 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) !50

Slide 51

Slide 51 text

Incrementing By One 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 52

Slide 52 text

Incrementing By One • ASSIGN: We assign the value (1) to the CV !0 • PRE_INC: We increment the CV !0 !52

Slide 53

Slide 53 text

Incrementing By One 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 54

Slide 54 text

Incrementing By One • 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) !54

Slide 55

Slide 55 text

Incrementing By One 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 56

Slide 56 text

Incrementing By One • ASSIGN: We assign the value (1) to the CV !0 • ASSIGN_ADD: We add 1 to the CV (!0) !56

Slide 57

Slide 57 text

Quoting Strings

Slide 58

Slide 58 text

Single Quoted Strings number of ops: 2 compiled vars: none line # * op etch ext return operands ---------------------------------------------------------- 2 0 ECHO 'bar' 3 1 > RETURN 1 echo  'bar';

Slide 59

Slide 59 text

Double Quoted Strings echo  "bar"; number of ops: 2 compiled vars: none line # * op etch ext return operands ---------------------------------------------------------- 2 0 ECHO 'bar' 3 1 > RETURN 1

Slide 60

Slide 60 text

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 61

Slide 61 text

Incrementing By One • 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 !61

Slide 62

Slide 62 text

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 63

Slide 63 text

Concatenation

Slide 64

Slide 64 text

Simple Concatenation 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 $a = "foo"; $a = $a . " bar";

Slide 65

Slide 65 text

Simple Concatenation • 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 !65

Slide 66

Slide 66 text

Assign-Concat 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 $a = "foo"; $a .= " bar";

Slide 67

Slide 67 text

Assign-Concat • ASSIGN: We assign the value (foo) to the CV !0 • ASSIGN_CONCAT: We concat the value “ bar” to the CV !0 !67

Slide 68

Slide 68 text

Function/Method Calls

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

Function Call with Arguments 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 bcadd(1, 3)

Slide 71

Slide 71 text

Function Call with Arguments • SEND_VAL: Queue up two arguments to send to the function • DO_FCALL: Call the function !71

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

Userland Function (cont.) 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

Slide 74

Slide 74 text

Userland Function (cont.) • 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 !74

Slide 75

Slide 75 text

Method Overloading (__call) 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 class foo { public function bar() {} public function __call($a, $b) {} } $foo = new foo; $foo->bar(); $foo->bat();

Slide 76

Slide 76 text

Method Overloading (__call) • 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 !76

Slide 77

Slide 77 text

Live Example

Slide 78

Slide 78 text

Feedback & Questions: Joind.in: https://joind.in/10286 Twitter: @dshafik Email: [email protected]

Slide 79

Slide 79 text

Tokens

Slide 80

Slide 80 text

OpCodes

Slide 81

Slide 81 text

VLD In-Depth VLD

Slide 82

Slide 82 text

Getters/Setters VS __get/__set VS no getter/setter

Slide 83

Slide 83 text

Magic Methods vs Native Methods __call __callStatic

Slide 84

Slide 84 text

$i++ vs ++$i vs + 1 vs += 1

Slide 85

Slide 85 text

Double vs Single Quotes Interpolation vs no-interpolation

Slide 86

Slide 86 text

Function Calls (Aliasing) Alias the one you call the most

Slide 87

Slide 87 text

@ operator

Slide 88

Slide 88 text

.= vs $string = $string . "foo"

Slide 89

Slide 89 text

+=, -=, etc

Slide 90

Slide 90 text

zvals

Slide 91

Slide 91 text

refcounting & garbage collection