PHP Performance I: Everything You Need to Know about OpCode Caches

Fee39f0c0ffb29d9ac21607ed188be6b?s=47 Davey Shafik
September 30, 2013

PHP Performance I: Everything You Need to Know about OpCode Caches

Fee39f0c0ffb29d9ac21607ed188be6b?s=128

Davey Shafik

September 30, 2013
Tweet

Transcript

  1. PHP Performance I Everything you need to know about OpCode

    caches
  2. •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 •@dshafik Davey Shafik
  3. What are OpCode Caches?

  4. What are OpCode Caches? • Performance enhancing extensions for PHP

    • Inject themselves into the execution life-cycle of PHP and cache as much of it as possible • Common to see a 300% speed increase with default settings! 4
  5. When Should I Use an OpCache?

  6. Always. No, really.

  7. Side Effects (I knew it couldn’t be that easy!)

  8. Cache Stampede

  9. Side Effects The primary side effect of using a cache

    is known as a cache stampede. This is where your cache expires while under heavy load. Caused by: • Restarting Server • Cache invalidation because of data edits • Newly provisioned servers being added to a cluster to help scale under load • Corruption (hardware issues)
  10. Which OpCode Cache

  11. Which OpCode Cache • There have been many caches, however

    till PHP 5.5 the dominant choice was the Alternative PHP Cache (APC) • With PHP 5.5, Zend open sourced it’s proprietary Zend OpCache and contributed it to the PHP project • Zend OpCache is now enabled by default with PHP 5.5+ unless disabled by --disable-all. • Zend OpCache is between 6-100% faster than APC 11
  12. Which OpCode Cache • PHP 5.3 or 5.4, choose between

    Zend OpCache and Alternative PHP Cache (APC) • PHP 5.5+, we recommend Zend OpCache • Zend OpCache can be used with PHP 5.2+ 12
  13. Which OpCode Cache • A second extension can be installed

    alongside Zend OpCache to give backwards compatibility with APC’s User Cache: APCu • APCu is APC with the OpCode Caching removed • 100% Backwards Compatible 13
  14. Installation

  15. Zend OpCache

  16. Installing Zend OpCache $ pecl install zendopcache-beta Add the following

    to your php.ini: [zendopcache] zend_extension=/full/path/to/opcache.so ; May be added by pecl opcache.memory_consumption=128 opcache.interned_strings_buffer=8 opcache.max_accelerated_files=4000 opcache.revalidate_freq=60 opcache.fast_shutdown=1 opcache.enable_cli=1 Restart PHP (php-fpm/apache)
  17. APC

  18. Installing APC $ pecl install apc-beta Add the following to

    your php.ini: [apc] extension=apc.so ; May be added by pecl apc.enabled=1 Restart PHP (php-fpm/apache)
  19. APCu (Optional) For APC User Cache Compatibility

  20. Installing APCu $ pecl install apcu-beta Add the following to

    your php.ini: [apcu] extension=apcu.so ; May be added by pecl apc.serializer=php ; See bug report for more info: http://ey.io/1aJhcOY Restart PHP (php-fpm/apache)
  21. Engine Yard PHP Performance Tools

  22. Engine Yard PHP Performance Tools • A suite of tools

    to aid with improving PHP performance • Open Source, Apache 2.0 License • On Github: http://github.com/engineyard/ey-php- performance-tools • Composer Compatible Package 22
  23. Installation Add composer.json configuration: "repositories": [ { "type": "vcs", "url":

    "http://github.com/engineyard/ey-php- performance-tools" } ], "require": { "php": ">=5.3.3", "engineyard/php-performance-tools": "dev-master" } Run composer update: $ composer.phar update engineyard/php-performance-tools
  24. Solving The Cache Stampede

  25. Priming The Cache

  26. Zend OpCache

  27. cache-primer

  28. cache-primer Create a config.php: <?php return [ /* URLs to

    cache */ 'urls' => [ 'http://ephernote.com/', 'http://ephernote.com/user/login', 'http://ephernote.com/user/register', 'http://ephernote.com/privary', // Note: this is a typo ], /* How many concurrent HTTP requests to make (requires the pecl_http extension) */ 'threads' => 3 ];
  29. cache-primer Run the primer: $ /path/composer/vendor/bin/cache-primer === Cache Primer ===

    Attempting to cache 4 URLs: Running in parallel with 3 concurrent requests ...! Cached 3 URLs in 0.6162059307098 seconds Encountered 1 errors
  30. cache-primer === Cache Primer === Attempting to cache 104 URLs:

    Running in parallel with 10 concurrent requests .......................................................... ..................!........................... Cached 103 URLs in 4.6162059307098 seconds Encountered 1 errors
  31. APC

  32. apc-primer

  33. Priming the Cache (APC) • Has built-in tools for priming

    the cache • Still requires a single HTTP request • Can cache more than just easily web accessible code • Any valid PHP can be cached, including partial view templates, global code, etc. • Use: engineyard/php-performance-tools/apc-primer 33
  34. apc-primer • Provides basic HTTP security • We recommend not

    relying on it, instead integrate it behind other authentication instead (e.g. embed it into your admin control panel) • Uses apc_compile_file() to add code to the cache • Requires a wrapper to expose via HTTP • Caches all .php and .phtml files not inside a tests directory 34
  35. apc-primer Create a config.php: <?php if (!defined('DS')) { define('DS', DIRECTORY_SEPARATOR);

    } return [ /* HTTP Basic Authentication: NOT RECOMMENDED */ 'auth' => [ 'enabled' => true, 'username' => 'admin', 'password' => 'changeme', ], /* Paths to cache (recursively) */ 'paths' => [ __DIR__ .DS. '..' .DS. '..' .DS. '..' .DS. 'config', __DIR__ .DS. '..' .DS. '..' .DS. '..' .DS. 'module', __DIR__ .DS. '..' .DS. '..' .DS. '..' .DS. 'vendor', __DIR__ .DS. '..' .DS. '..' .DS. '..' .DS. 'public', ] ];
  36. apc-primer Place a simple wrapper in your web-root, e.g. /public/admin/apc-cache-primer.php

    <?php // public/admin/apc-cache-primer.php require '../../vendor/engineyard/php-performance- tools/apc-primer/apc-primer.php'; Request the file via HTTP: Cached 4841 files in 2.1302671432495 seconds
  37. Under the Hood OR: Here be dragons!

  38. Execution Life-Cycle

  39. Execution Life-cycle: PHP vs Java 39

  40. Execution Lifecycle with an OpCode Cache

  41. Tokens & OpCodes

  42. The Worst Hello World, Ever <?php class Greeting { public

    function sayHello($to) { echo "Hello $to"; } } $greeter = new Greeting(); $greeter->sayHello("World"); ?>
  43. Tokenizing

  44. 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_WHITESPACE 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_STRING "World" ) ; T_WHITESPACE T_CLOSE_TAG ?> Tokenizing
  45. 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. 45 " T_ENCAPSED_AND_WHITESPACE Hello T_VARIABLE $to " T_CONSTANT_ENCAPSED_STRING "World"
  46. Compiling

  47. VLD Vulcan Logic Dumper

  48. 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 <file> 48
  49. VLD — Vulcan Logic Dumper • Outputs, in order: •

    global code (main script) • global functions • class functions 49
  50. Understanding VLD Dumps Class Greeting: Function sayhello: filename: ./Greeting.php function

    name: sayHello Header compiled vars: !0 = $to number of ops: 8
  51. 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) 51 line # * op fetch ext return operands ----------------------------------------------------------------
  52. VLD — Variables • Four types of variables 1. Exclamation

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

    !0 = $to line # * op fetch ext return operands ---------------------------------------------------------------- 3 0 > EXT_NOP 1 RECV !0 5 2 EXT_STMT 3 ADD_STRING ~0 'Hello+' 4 ADD_VAR ~0 ~0, !0 5 ECHO ~0 6 6 EXT_STMT 7 > RETURN null class Greeting { public function sayHello($to) { echo "Hello $to"; } }
  54. 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 nothing as the function ends 54
  55. Main Script compiled vars: !0 = $greeter line # *

    op fetch ext return operands -------------------------------------------------------------------- 2 0 > EXT_STMT 1 NOP 9 2 EXT_STMT 3 FETCH_CLASS 4 :1 'Greeting' 4 EXT_FCALL_BEGIN 5 NEW $2 :1 6 DO_FCALL_BY_NAME 0 7 EXT_FCALL_END 8 ASSIGN !0, $2 10 9 EXT_STMT 10 INIT_METHOD_CALL !0, 'sayHello' 11 EXT_FCALL_BEGIN 12 SEND_VAL 'World' 13 DO_FCALL_BY_NAME 1 14 EXT_FCALL_END 12 15 EXT_STMT 16 > RETURN 1 $greeter = new Greeting(); $greeter->sayHello("World");
  56. 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. 56
  57. 57 Resources • Zend OpCache Source Code: http://ey.io/17Qt6W9 • Zend

    OpCache PECL Package: http://ey.io/18h2Inp • APC Source Code: http://ey.io/18gZ9iW • APC PECL Package: http://ey.io/15JuQkp • Understanding Opcodes (Sara Golemon): http://ey.io/19q32iD • More source analysis with VLD (Derick Rethans): http://ey.io/bx0q8N http://ey.io/opcaches
  58. http://ey.io/opcaches Feedback & Questions: Twitter: @dshafik Email: davey@engineyard.com