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

[Lonestar PHP] Fast, Not Furious: How to identify and fix slow code

[Lonestar PHP] Fast, Not Furious: How to identify and fix slow code

There's a saying "hardware is cheaper than developers", but this only holds true if you treat your hardware with the respect it deserves.

Learn how to diagnose, confirm and fix bottlenecks in your application using new tools like XHProf and tried and true tools like xdebug.

Also, we'll take an in-depth look at apc (advanced PHP cache) and memcache; even if you know and use memcache, this one can probably teach you a thing or two.

Fee39f0c0ffb29d9ac21607ed188be6b?s=128

Davey Shafik

June 30, 2012
Tweet

Transcript

  1. Fast, Not Furious How to identify and fix slow code

    1
  2. •Engineer at Engine Yard for Orchestra.io PHP Platform as a

    Service (PaaS) •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 and PHP internals •@dshafik •(Buy My Books!) Davey Shafik 2
  3. About These Slides 5

  4. 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 6
  5. Hardware is Cheap, Programmers are Expensive - Jeff Atwood 7

  6. But only if you respect the hardware. - Me 8

  7. Anecdote OR: why you shouldn’t randomly optimize — and why

    you should be careful with PHP 5.3’s memory garbage collection 9
  8. Common Causes of Slowdowns 10

  9. 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. 11
  10. Do You Have a Problem? 12

  11. 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? 13
  12. The Performance Loop 14

  13. Benchmark Profile Make Changes 15

  14. But isn’t Profiling the same as Benchmarking? 16

  15. 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 17
  16. Profiling with xhprof 18

  17. Grab the tarball: $ wget http://pecl.php.net/get/ xhprof-0.9.2.tgz $ tar –zxvf

    xhprof-0.9.2.tgz Change to the sub-directory: $ cd xhprof-0.9.2/extension Compile: $ ./configure --enable-xhprof $ make $ make install Installing xhprof 19
  18. Update php.ini: [xhprof] extension=xhprof.so xhprof.output_dir="/tmp/xhprof" Restart your httpd. Installing xhprof

    (cont.) 20
  19. Clone from git: $ git clone git://github.com/preinheimer/xhprof.git Setup the correct

    Database adapter: $ cd xhprof/xhprof_lib/utils $ rm xhprof_runs.php $ ln –s xhprof_runs_mysql.php xhprof_runs.php Installing xh-gui 21
  20. Create the Database: CREATE TABLE `details` ( `id` char(17) NOT

    NULL, `url` varchar(255) default NULL, `c_url` varchar(255) default NULL, `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, `server name` varchar(64) default NULL, `perfdata` MEDIUMBLOB, `type` tinyint(4) default NULL, `cookie` BLOB, `post` BLOB, `get` BLOB, `pmu` int(11) default NULL, `wt` int(11) default NULL, `cpu` int(11) default NULL, `server_id` char(3) NOT NULL default 't11', `aggregateCalls_include` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `url` (`url`), KEY `c_url` (`c_url`), KEY `cpu` (`cpu`), KEY `wt` (`wt`), KEY `pmu` (`pmu`), KEY `timestamp` (`timestamp`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; Installing xh-gui 22
  21. Update the config: $ cd .. # back up to

    xhprof_lib $ cp config.sample.php config.php Change these: $_xhprof['dbhost'] = 'localhost'; $_xhprof['dbuser'] = 'username'; $_xhprof['dbpass'] = 'password'; $_xhprof['dbname'] = 'xhprof'; $_xhprof['servername'] = 'myserver'; $_xhprof['namespace'] = 'myapp'; $_xhprof['url'] = 'http://url/to/xhprof/xhprof_html'; Installing xh-gui 23
  22. Setup Httpd: Apache: <VirtualHost *:80> ServerName xh.dev DocumentRoot /path/to/xhprof/xhprof_html </VirtualHost>

    Nginx: server { listen 80 default_server; server_name xh.dev; root /path/to/xhprof/xhprof_html; index index.php; } Installing xh-gui 24
  23. Using xhprof/xh-gui 25

  24. Add prepend/append config to php.ini: auto_prepend_file = "/path/to/xhprof/external/header.php" auto_append_file =

    "/path/to/xhprof/external/footer.php" OR Apache VirtualHost: php_admin_value auto_prepend_file /path/to/xhprof/external/header.php php_admin_value auto_append_file /path/to/xhprof/external/footer.php Using xhprof/xh-gui 26
  25. Demo 27

  26. function _urlSimilartor($url) { $url = preg_replace("/[0-9]+/", "XXX", $url); $qs =

    parse_url($url, PHP_URL_QUERY); if ($qs) { $parts = array(); parse_str($qs, $parts); $values = array_pad(array(), sizeof($parts), "XXX"); $normalized = array_combine(array_keys($parts), $values); $replace = http_build_query($normalized); $url = str_replace($qs, $replace, $url); } $url = preg_replace("![?&]_profile=\d!", "", $url); return $url; } URL Similartor 28
  27. Caching 29

  28. Key-Value Stores 30

  29. Key-Value Stores • APC • memcache • Cassandra • redis

    • Most NoSQL implementations (CouchDB, MongoDB, MemBase) 31
  30. Memcache Namespacing 32

  31. Namespace 1 Data Data Data Data Data Data Data Data

    Data Data Data Data Namespace 2 Data Data Data Data Data Data Data Data Data Data Data Data Memcache Namespaces 33
  32. Namespace 1 Data Data Data Data Data Data Data Data

    Data Data Data Data Namespace 2 Data Data Data Data Data Data Data Data Data Data Data Data Memcache Namespaces 33
  33. • Create a namespace key in memcache if there isn’t

    one • memcache_set('mynamespace', rand(1, 1000)); • Use the namespace name, value, and the items unique key to create a key • mynamespace_189_a1773d62a609dd09e98ea1aebeddbd94 • When you want to clear the cache, increment the namespace key • memcache_increment(‘mynamespace’); The Code 34
  34. Namespace 1 Namespace 1.1 Namespace 1.2 Namespace 1.3 Namespace 1.4

    Data Data Data Data Data Data Data Data Data Data Data Data Data Data Data Data Data Data Data Data Data Data Data Data Memcache Namespace Segmenting 35
  35. The Code • Create a segment key in memcache if

    there isn’t one • memcache_set('mynamespace_config', rand(1, 1000)); • Use the namespace name, value, segment name, value and the items unique key to create a key • mynamespace_189_config_4_a609dd09e98ea1aebeddbd94 • When you want to clear the segment cache, increment the segment key • memcache_increment('mynamespace_config'); 36
  36. example.org example.org_session example.org_public example.org_admin example.org_config Data Data Data Data Data

    Data Data Data Data Data Data Data Data Data Data Data Data Data Data Data Data Data Data Data Practical Cache Layout 37
  37. require_once 'Cache/Memcache.php'; $cache = new Cache_Memcache(); $key = $_SERVER['REQUEST_URI']; $data

    = $cache->get($key, 'blog-pages'); if ($data !== false) { echo $data; } else { ob_start(); // output all the data to the buffer … // Assign to $data and actually output $data = ob_get_flush(); // Cache it $cache->set($key, $data, 'blog-pages'); } Using The Code 38
  38. require_once 'Cache/Memcache.php'; $cache = new Cache_Memcache(); $cache->clearCache('blog-pages'); Clearing the Cache

    39
  39. Other Memcache Tricks 40

  40. Other Memcache Tricks • Meta Key • json string containing:

    • Last modified date • # of slabs • Use date to send Last-Modified header and 304 Not Modified Status • Use # of slabs to split content across multiple keys if > 1MB in size 41
  41. Other Memcache Tricks Key: mynamespace_189_a1773d62a609dd09e98ea1aebeddbd94_meta { /* RFC 1123 */

    lastModified: "Fri, 29 Jun 2012 00:30:25 -0400", slabs: 2 } 42
  42. The Code 43

  43. APC Use it. 44

  44. OR: Here be dragons Getting Inside PHP 45

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

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

    class } T_WHITESPACE T_WHITESPACE T_STRING Greeting } T_WHITESPACE T_WHITESPACE { T_VARIABLE $greeter T_WHITESPACE T_WHITESPACE T_PUBLIC public = T_WHITESPACE T_WHITESPACE T_FUNCTION function T_NEW new T_WHITESPACE T_WHITESPACE T_STRING sayHello T_STRING Greeting ( ( T_VARIABLE $to ) ) ; T_WHITESPACE T_WHITESPACE { T_VARIABLE $greeter T_WHITESPACE T_OBJECT_OPERATOR -> T_ECHO echo T_STRING sayHello T_WHITESPACE ( " T_CONSTANT_ENCAPSED_STRING "World" T_ENCAPSED_AND_WHITESPACE Hello ) T_VARIABLE $to ; " T_WHITESPACE ; T_CLOSE_TAG ?> 47
  47. Execution Lifecycle: PHP vs Java 48

  48. Execution Lifecycle with APC 49

  49. Installing APC •$ pecl install apc •Add: extension=apc.so to php.ini

    •Done. What’s your excuse? 50
  50. Thank You! •Feedback: • https://joind.in/talk/view/6349 • @dshafik • davey@engineyard.com •

    Slides: • http://daveyshafik.com/slides 51