Profile your PHP application and make it fly

  • 43,471 views
Uploaded on

Making an application scale and go faster is often seen as a wizardly task. We read the micro-optimisation tricks posted in tech blogs and apply them with unconditional trust and great hope, and then …

Making an application scale and go faster is often seen as a wizardly task. We read the micro-optimisation tricks posted in tech blogs and apply them with unconditional trust and great hope, and then wonder why performances haven't improved that much ("Wait, I even replaced 'print' with 'echo'!!!").

In this talk we'll see how we can take easy, practical steps we can apply over and over that really make a difference, by analysing what our application does under the hood, measuring how and where the different resources are used, eliminating the real bottlenecks and restructuring critical components to handle growing loads.

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
  • Hi Lorenzo! I'm Manuel, from PHPBarcelona. Would it be possible to download this fantastic slideshow of yours!!

    Thanks and hope to see you at PHPUK Conf 2011 :-)
    Are you sure you want to
    Your message goes here
  • Thanks A Lot. Loved it.
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
43,471
On Slideshare
0
From Embeds
0
Number of Embeds
11

Actions

Shares
Downloads
0
Comments
2
Likes
54

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide
































































































Transcript

  • 1. Lorenzo Alberton Profile your PHP application and make it fly The path to performance and scalability PHPNW, Saturday 9th October 2010 1
  • 2. Quiz time! Which is faster? 2
  • 3. Quiz time! Which is faster? (A) echo (B) print 2
  • 4. Quiz time! Which is faster? (A) echo single (B) print double quotes quotes 2
  • 5. Answer It probably doesn’t matter. 3
  • 6. Awareness is the Know your targets Know what it will take to meet them Know your constraints 4
  • 7. Tools: Profilers Tools are just tools They are absolutely essential to doing your job They will never do your job for you Tools will never replace experience and discipline But tools can help you maintain discipline Theo Schlossnagle 5
  • 8. Xdebug + xCacheGrind http://xdebug.org/ http://valgrind.org/ http://kcachegrind.sf.net/ http://sf.net/projects/wincachegrind http://code.google.com/p/webgrind/ http://www.maccallgrind.com/ 6
  • 9. XHProf XHProf http://mirror.facebook.net/facebook/xhprof/ http://pecl.php.net/package/xhprof http://techportal.ibuildings.com/2009/12/01/profiling-with-xhprof/ 7
  • 10. XHProf UI Report - Web interface 8
  • 11. XHProf UI Report - Web interface 8
  • 12. XHProf UI Report - Web interface 8
  • 13. XHProf UI Report - Web interface Function Name Calls Wall Time (inclusive / exclusive) CPU (inclusive / exclusive) Memory (inclusive / exclusive) Peak Memory Use (inclusive / exclusive) # and % 8
  • 14. XHProf UI - Trace calls 9
  • 15. XHProf UI - Call Graph 10
  • 16. XHProf UI - Call Graph 10
  • 17. Measuring Analyse the framework Analyse your application 11
  • 18. Know Your Framework Measure the baseline Shortcuts aren’t always “free” When there are two ways of doing something, usually one is much better Loading (classes, resources) is expensive 12
  • 19. Zend Framework Baseline $ zf.sh create project testzf 13
  • 20. Know Your Framework Measure the baseline Shortcuts aren’t always “free” When there are two ways of doing something, usually one is much better Loading (classes, resources) is expensive 14
  • 21. Shortcuts - The Real Cost public function doSomething(array $data) { // Zend_Config object $config = $this->getConfig(); $var = $config->aaa->bbb->ccc; // ... } 15
  • 22. Shortcuts - The Real Cost public function doSomething(array $data) { // Zend_Config object $config = $this->getConfig(); $var = $config->aaa->bbb->ccc; // ... } 15
  • 23. Shortcuts - The Real Cost public function doSomething(array $data) { // Zend_Config object $config = $this->getConfig(); $var = $config->aaa->bbb->ccc; // ... } 15
  • 24. Shortcuts - The Real Cost public function doSomething(array $data) { // Zend_Config object $config = $this->getConfig(); $var = $config->aaa->bbb->ccc; // ... } 2 1 3 15
  • 25. Know Your Framework Measure the baseline Shortcuts aren’t always “free” When there are two ways of doing something, usually one is much better Loading (classes, resources) is expensive 16
  • 26. partial() vs. render() <!-- mytemplate.phtml --> <ul> <?php foreach ($this->rows as $row) { $this->partial(‘listItemTemplate.phtml’, ‘mymodule’, array(‘data’ => $row); } ?> </ul> 17
  • 27. partial() vs. render() 18
  • 28. partial() vs. render() 18
  • 29. partial() vs. render() 18
  • 30. partial() vs. render() 18
  • 31. partial() vs. render() 18
  • 32. partial() vs. render() 18
  • 33. partial() vs. render() 18
  • 34. partial() vs. render() <!-- mytemplate.phtml --> <ul> <?php foreach ($this->rows as $row) { $this->partial(‘listItemTemplate.phtml’, ‘mymodule’, array(‘data’ => $row); } ?> </ul> 19
  • 35. partial() vs. render() <!-- mytemplate.phtml --> <ul> <?php foreach ($this->rows as $row) { $this->row) { $this->render(‘listItemTemplate.phtml’); $this->partial(‘listItemTemplate.phtml’, } ‘mymodule’, array(‘data’ => $row); ?> } </ul> ?> </ul> 19
  • 36. Know Your Framework Measure the baseline Shortcuts aren’t always “free” When there are two ways of doing something, usually one is much better Loading (classes, resources) is expensive 20
  • 37. Plugins / Helpers <!-- mytemplate.phtml --> <ul> <?php foreach ($this->rows as $row) { echo $this->myViewHelper($row); } ?> </ul> 21
  • 38. Plugins / Helpers // calling: // Zend_View_Abstract::__call() <!-- mytemplate.phtml --> // Zend_View_Abstract::getHelper() <ul> // Zend_View_Abstract::_getPlugin() <?php // Zend_View_Abstract::_getPluginLoader() // Zend_Loader_PluginLoader::load() foreach ($this->rows as $row) { // Zend_Loader_PluginLoader::isLoaded() echo $this->myViewHelper($row); // Zend_Loader_PluginLoader::getClassName() } // Zend_Loader_PluginLoader::_formatName() // Zend_View_Abstract::setView() ?> // call_user_func_array() </ul> // My_Helpers_MyViewHelper::myViewHelper() 21
  • 39. Plugins / Helpers <!-- mytemplate.phtml --> <ul> <?php foreach ($this->rows as $row) { echo $this->myViewHelper($row); } ?> </ul> 21
  • 40. Plugins / Helpers <!-- mytemplate.phtml --> <ul> <?php foreach $helper = $this->getHelper('myViewHelper'); ($this->rows as $row) { echo $this->myViewHelper($row); foreach } ($this->rows as $row) { echo $helper->myViewHelper($row); } ?> </ul> ?> </ul> 21
  • 41. Know Your Application Every feature has a cost. Measure it. 22
  • 42. Optimisation Techniques Tips and tricks to get faster applications 23
  • 43. Rules of thumb Profile / Measure Sort by [ Excl. CPU | Memory | Time | Calls ] Start from the TOP of the list Analyse, refactor, optimise Measure improvement Start over 24
  • 44. Rules of thumb Profile / Measure Sort by [ Excl. CPU | Memory | Time | Calls ] Start from the TOP of the list Analyse, refactor, optimise Measure improvement Start over Again, and again, and again. 24
  • 45. Profiling as Design validation practice 25
  • 46. Easy performance targets External resources (http, db, disk I/O) Memory-hungry function (xml/json parsers, output buffering) Loops Regular expressions Wrappers 26
  • 47. Refactoring hints PREPARE SCREEN showing db calls 27
  • 48. Refactoring hints PREPARE SCREEN showing db calls 27
  • 49. Refactoring hints 28
  • 50. Refactoring hints 28
  • 51. Refactoring hints 28
  • 52. Refactoring hints 29
  • 53. Refactoring hints PHP is FAST 29
  • 54. Refactoring hints PHP is FAST ...but do you really need to call strtolower() 15000 times? 29
  • 55. Refactoring hints PHP is FAST ...but do you really need to call strtolower() 15000 times? Re-think why you’re doing something, if it’s the right place to do it, and if possible reduce the amount of data you need to process 29
  • 56. Code smells Immutable functions called within loops Same content being generated twice Content that does not change being generated every time Content being generated even if not used 30
  • 57. Code smells Immutable functions called within loops Same content being generated twice Content that does not change being generated every time Content being generated even if not used Sometimes it’s less obvious than it might seem 30
  • 58. Caching (computational reuse) Static variables APC / Zend Server Cache / ... Memcached Reverse proxies Browser 31
  • 59. Caching (computational reuse) Static variables APC / Zend Server Cache / ... Memcached Reverse proxies Browser Do no work at all, if you can avoid it 31
  • 60. [Cache] Layers! 32
  • 61. System calls strace / ltrace / dtrace / ktrace / truss # httpd -X & [1] 5772 # strace -p 5772 -o /tmp/strace.out Process 5772 attached - interrupt to quit # curl http://mysite.local/ # less /tmp/strace.out 33
  • 62. System calls lstat("/mnt", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 strace / ltrace / dtrace / ktrace / truss lstat("/mnt/hgfs", {st_mode=S_IFDIR|0555, st_size=4192, ...}) = 0 lstat("/mnt/hgfs/service_layer", {st_mode=S_IFDIR|0755, st_size=170, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches", {st_mode=S_IFDIR|0755, st_size=102, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9", {st_mode=S_IFDIR|0755, st_size=238, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9/php", {st_mode=S_IFDIR|0755, st_size=306, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9/php/library", {st_mode=S_IFDIR|0755, st_size=136, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9/php/library/Zend", 0x7fff89049af0) = -1 ENOENT (No such file or directory) open("/mnt/hgfs/service_layer/branches/1.9/php/library/Zend/Controller/Plugin/Abstract.php", O_RDONLY) = -1 ENOENT (No such file or directory) lstat("/mnt", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/mnt/hgfs", {st_mode=S_IFDIR|0555, st_size=4192, ...}) = 0 lstat("/mnt/hgfs/service_layer", {st_mode=S_IFDIR|0755, st_size=170, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches", {st_mode=S_IFDIR|0755, st_size=102, ...}) = 0 # httpd -X & lstat("/mnt/hgfs/service_layer/branches/1.9", {st_mode=S_IFDIR|0755, st_size=238, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9/php", {st_mode=S_IFDIR|0755, st_size=306, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9/php/application", {st_mode=S_IFDIR|0755, st_size=340, ...}) = 0 [1] 5772 lstat("/mnt/hgfs/service_layer/branches/1.9/php/application/models", {st_mode=S_IFDIR|0755, st_size=306, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9/php/application/models/Zend", 0x7fff89049af0) = -1 ENOENT (No such file or directory) open("/mnt/hgfs/service_layer/branches/1.9/php/application/models/Zend/Controller/Plugin/Abstract.php", O_RDONLY) = -1 ENOENT (No such file or directory) getcwd("/mnt/hgfs/service_layer/branches/1.9/php/public"..., 4096) = 48 lstat("/mnt", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/mnt/hgfs", {st_mode=S_IFDIR|0555, st_size=4192, ...}) = 0 lstat("/mnt/hgfs/service_layer", {st_mode=S_IFDIR|0755, st_size=170, ...}) = 0 # strace -p 5772 -o /tmp/strace.out lstat("/mnt/hgfs/service_layer/branches", {st_mode=S_IFDIR|0755, st_size=102, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9", {st_mode=S_IFDIR|0755, st_size=238, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9/php", {st_mode=S_IFDIR|0755, st_size=306, ...}) = 0 Process 5772 attached - interrupt to quit lstat("/mnt/hgfs/service_layer/branches/1.9/php/public", {st_mode=S_IFDIR|0755, st_size=340, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9/php/public/Zend", 0x7fff89049af0) = -1 ENOENT (No such file or directory) open("/mnt/hgfs/service_layer/branches/1.9/php/public/Zend/Controller/Plugin/Abstract.php", O_RDONLY) = -1 ENOENT (No such file or directory) lstat("/var", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/var/www", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/var/www/commonlib", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/var/www/commonlib/current", {st_mode=S_IFLNK|0777, st_size=32, ...}) = 0 readlink("/var/www/commonlib/current", "/mnt/hgfs/commonlib/branches/1.9"..., 4096) = 32 # curl http://mysite.local/ lstat("/mnt", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/mnt/hgfs", {st_mode=S_IFDIR|0555, st_size=4192, ...}) = 0 lstat("/mnt/hgfs/commonlib", {st_mode=S_IFDIR|0755, st_size=170, ...}) = 0 lstat("/mnt/hgfs/commonlib/branches", {st_mode=S_IFDIR|0755, st_size=136, ...}) = 0 lstat("/mnt/hgfs/commonlib/branches/1.9", {st_mode=S_IFDIR|0755, st_size=272, ...}) = 0 lstat("/mnt/hgfs/commonlib/branches/1.9/Zend", 0x7fff89049af0) = -1 ENOENT (No such file or directory) open("/var/www/commonlib/current/Zend/Controller/Plugin/Abstract.php", O_RDONLY) = -1 ENOENT (No such file or directory) lstat("/usr", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 # less /tmp/strace.out lstat("/usr/local", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/usr/local/zend", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/usr/local/zend/share", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/usr/local/zend/share/ZendFramework", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/usr/local/zend/share/ZendFramework/library", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/usr/local/zend/share/ZendFramework/library/Zend", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/usr/local/zend/share/ZendFramework/library/Zend/Controller", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/usr/local/zend/share/ZendFramework/library/Zend/Controller/Plugin", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/usr/local/zend/share/ZendFramework/library/Zend/Controller/Plugin/Abstract.php", {st_mode=S_IFREG|0644, st_size=4298, ...}) = 0 open("/usr/local/zend/share/ZendFramework/library/Zend/Controller/Plugin/Abstract.php", O_RDONLY) = 40 33
  • 63. System calls lstat("/mnt", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 strace / ltrace / dtrace / ktrace / truss lstat("/mnt/hgfs", {st_mode=S_IFDIR|0555, st_size=4192, ...}) = 0 lstat("/mnt/hgfs/service_layer", {st_mode=S_IFDIR|0755, st_size=170, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches", {st_mode=S_IFDIR|0755, st_size=102, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9", {st_mode=S_IFDIR|0755, st_size=238, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9/php", {st_mode=S_IFDIR|0755, st_size=306, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9/php/library", {st_mode=S_IFDIR|0755, st_size=136, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9/php/library/Zend", 0x7fff89049af0) = -1 ENOENT (No such file or directory) open("/mnt/hgfs/service_layer/branches/1.9/php/library/Zend/Controller/Plugin/Abstract.php", O_RDONLY) = -1 ENOENT (No such file or directory) ... lstat("/mnt", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/mnt/hgfs", {st_mode=S_IFDIR|0555, st_size=4192, ...}) = 0 lstat("/mnt/hgfs/service_layer", {st_mode=S_IFDIR|0755, st_size=170, ...}) = 0 getcwd("/var/www/site/php/public"..., 4096) = 48 lstat("/mnt/hgfs/service_layer/branches", {st_mode=S_IFDIR|0755, st_size=102, ...}) = 0 # httpd -X & lstat("/mnt/hgfs/service_layer/branches/1.9", {st_mode=S_IFDIR|0755, st_size=238, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9/php", {st_mode=S_IFDIR|0755, st_size=306, ...}) = 0 lstat("/var", {st_mode=S_IFDIR|0755, st_size=4096}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9/php/application", {st_mode=S_IFDIR|0755, st_size=340, ...}) = 0 [1] 5772 lstat("/mnt/hgfs/service_layer/branches/1.9/php/application/models", {st_mode=S_IFDIR|0755, st_size=306, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9/php/application/models/Zend", 0x7fff89049af0) = -1 ENOENT (No such file or directory) lstat("/var/www", {st_mode=S_IFDIR|0555, st_size=4192}) = 0 open("/mnt/hgfs/service_layer/branches/1.9/php/application/models/Zend/Controller/Plugin/Abstract.php", O_RDONLY) = -1 ENOENT (No such file or directory) getcwd("/mnt/hgfs/service_layer/branches/1.9/php/public"..., 4096) = 48 lstat("/var/www/site", {st_mode=S_IFDIR|0755, st_size=170}) = 0 lstat("/mnt", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/mnt/hgfs", {st_mode=S_IFDIR|0555, st_size=4192, ...}) = 0 lstat("/mnt/hgfs/service_layer", {st_mode=S_IFDIR|0755, st_size=170, ...}) = 0 lstat("/var/www/site/php", -o /tmp/strace.out # strace -p 5772 {st_mode=S_IFDIR|0755, st_size=306}) = 0 lstat("/mnt/hgfs/service_layer/branches", {st_mode=S_IFDIR|0755, st_size=102, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9", {st_mode=S_IFDIR|0755, st_size=238, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9/php", {st_mode=S_IFDIR|0755, st_size=306, ...}) = 0 lstat("/var/www/site/php/public", {st_mode=S_IFDIR|0755, st_size=340}) = 0 Process 5772 attached - interrupt to quit lstat("/mnt/hgfs/service_layer/branches/1.9/php/public", {st_mode=S_IFDIR|0755, st_size=340, ...}) = 0 lstat("/mnt/hgfs/service_layer/branches/1.9/php/public/Zend", 0x7fff89049af0) = -1 ENOENT (No such file or directory) lstat("/var/www/site/php/public/Zend", 0x7fff89049af0) = -1 ENOENT open("/mnt/hgfs/service_layer/branches/1.9/php/public/Zend/Controller/Plugin/Abstract.php", O_RDONLY) = -1 ENOENT (No such file or directory) lstat("/var", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/var/www", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 (No such file or directory) lstat("/var/www/commonlib", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/var/www/commonlib/current", {st_mode=S_IFLNK|0777, st_size=32, ...}) = 0 readlink("/var/www/commonlib/current", "/mnt/hgfs/commonlib/branches/1.9"..., 4096) = 32 open("/var/www/site/php/public/Zend/Controller/Plugin/Abstract.php", # curl http://mysite.local/ lstat("/mnt", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/mnt/hgfs", {st_mode=S_IFDIR|0555, st_size=4192, ...}) = 0 O_RDONLY) = -1 ENOENT (No such file or directory) lstat("/mnt/hgfs/commonlib", {st_mode=S_IFDIR|0755, st_size=170, ...}) = 0 lstat("/mnt/hgfs/commonlib/branches", {st_mode=S_IFDIR|0755, st_size=136, ...}) = 0 lstat("/mnt/hgfs/commonlib/branches/1.9", {st_mode=S_IFDIR|0755, st_size=272, ...}) = 0 lstat("/mnt/hgfs/commonlib/branches/1.9/Zend", 0x7fff89049af0) = -1 ENOENT (No such file or directory) ... open("/var/www/commonlib/current/Zend/Controller/Plugin/Abstract.php", O_RDONLY) = -1 ENOENT (No such file or directory) lstat("/usr", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 # less /tmp/strace.out lstat("/usr/local", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/usr/local/zend", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/usr/local/zend/share", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/usr/local/zend/share/ZendFramework", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/usr/local/zend/share/ZendFramework/library", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/usr/local/zend/share/ZendFramework/library/Zend", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/usr/local/zend/share/ZendFramework/library/Zend/Controller", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/usr/local/zend/share/ZendFramework/library/Zend/Controller/Plugin", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat("/usr/local/zend/share/ZendFramework/library/Zend/Controller/Plugin/Abstract.php", {st_mode=S_IFREG|0644, st_size=4298, ...}) = 0 open("/usr/local/zend/share/ZendFramework/library/Zend/Controller/Plugin/Abstract.php", O_RDONLY) = 40 33
  • 64. System calls # grep 'stat' /tmp/strace.out | wc -l 20511 # /usr/bin/strace -p 5772 -c Process 5772 attached - interrupt to quit Process 5772 detached % time seconds usecs/call calls errors syscall ------ ---------- ---------- ------ ------ ------- 50.86 0.131501 7 20054 2029 lstat 28.42 0.073497 742 99 76 access 16.78 0.043385 32 1337 1095 open 1.61 0.004167 5 798 1 read 0.94 0.002436 13 193 getcwd ... ------ ---------- ---------- ------ ------ ------- 100.00 0.020951 1267 103 total 34
  • 65. System calls # grep 'stat' /tmp/strace.out | wc -l 20511 # /usr/bin/strace -p 5772 -c Process 5772 attached - interrupt to quit Process 5772 detached % time seconds usecs/call calls errors syscall ------ ---------- ---------- ------ ------ ------- 50.86 0.131501 7 20054 2029 lstat 28.42 0.073497 742 99 76 access 16.78 0.043385 32 1337 1095 open 1.61 0.004167 5 798 1 read 0.94 0.002436 13 193 getcwd ... ------ ---------- ---------- ------ ------ ------- 100.00 0.020951 1267 103 total 34
  • 66. Fix include_path (disk I/O) set_include_path(implode(PATH_SEPARATOR, array( ‘../library/’, // project library classes ‘./models/’, // project models ‘/usr/local/php/share/’, // framework )); The order (and number!) of the include paths is important. Tip: increase the realpath_cache size. 35
  • 67. Fix include_path (disk I/O) set_include_path(implode(PATH_SEPARATOR, array( ‘../library/’, // project// framework ‘/usr/local/php/share/’, library classes ‘./models/’, ‘../library/’, // project models classes library ‘/usr/local/php/share/’, // framework ‘./models/’, // project models )); The order (and number!) of the include paths is important. Tip: increase the realpath_cache size. 35
  • 68. Apache config open("/var/www/.htaccess", O_RDONLY) = -1 ENOENT open("/var/www/php/.htaccess", O_RDONLY) = -1 ENOENT open("/var/www/php/site/.htaccess", O_RDONLY) = -1 ENOENT open("/var/www/php/site/org/.htaccess", O_RDONLY) = -1 ENOENT open("/var/www/php/site/org/view/.htaccess", O_RDONLY) = -1 ENOENT ... open("/var/www/php/site/index.html", O_RDONLY) = -1 ENOENT open("/var/www/php/site/index.cgi", O_RDONLY) = -1 ENOENT open("/var/www/php/site/index.php", O_RDONLY) = 0 ... open("/var/www/php/site/favicon.ico", O_RDONLY) = -1 ENOENT Turn AllowOverride off Optimise DirectoryIndex Add favicon.cio 36
  • 69. Fix autoloader (disk I/O) function __autoload($className) { if (class_exists($className, false)) return; if ( (($file = apc_fetch($className)) === false) || ($file === null)) { $classFile = str_replace(‘_’, DIRECTORY_SEPARATOR, $className) . ‘.php’; $file = stream_resolve_include_path($classFile); if ($file !== false) { apc_store($className, $file, 86400); } else { apc_store($className, null, 86400); } } if (($file !== false) && ($file !== null)) { include $file; } } 37
  • 70. Fix autoloader (disk I/O) function __autoload($className) { if (class_exists($className, false)) return; if ( (($file = apc_fetch($className)) === false) || ($file === null)) { $classFile = str_replace(‘_’, DIRECTORY_SEPARATOR, $className) . ‘.php’; $file = stream_resolve_include_path($classFile); if ($file !== false) { apc_store($className, $file, 86400); } else { apc_store($className, null, 86400); } } if (($file !== false) && ($file !== null)) { include $file; } } 37
  • 71. Fix autoloader (disk I/O) function __autoload($className) { if (class_exists($className, false)) return; if ( (($file = apc_fetch($className)) === false) || ($file === null)) { $classFile = str_replace(‘_’, DIRECTORY_SEPARATOR, $className) . ‘.php’; $file = stream_resolve_include_path($classFile); if ($file !== false) { apc_store($className, $file, 86400); } else { apc_store($className, null, 86400); } } if (($file !== false) the ($file !== null)) { Rely on && autoloader. include $file; } Avoid explicit includes } http://devzone.zend.com/article/4525 37
  • 72. OPCode cache APC XCache Zend Accelerator WinCache eAccelerator ... 38
  • 73. OPCode cache APC XCache Zend Accelerator WinCache eAccelerator ... # grep 'open(' /tmp/strace.out open("/usr/local/php/share/Zend/Log.php", O_RDONLY) = 40 open("/usr/local/php/share/Zend/Form.php", O_RDONLY) = 40 open("/usr/local/php/share/Zend/Locale.php", O_RDONLY) = 40 38
  • 74. OPCode cache APC XCache Zend Accelerator WinCache eAccelerator ... # grep 'open(' /tmp/strace.out open("/usr/local/php/share/Zend/Log.php", O_RDONLY) = 40 open("/usr/local/php/share/Zend/Form.php", O_RDONLY) = 40 open("/usr/local/php/share/Zend/Locale.php", O_RDONLY) = 40 Conditional includes may cause OPCode cache trashing 38
  • 75. Include Hierarchy - PECL inclued http://pecl.php.net/package/inclued 39
  • 76. Watch your error log! Warning: Undefined index: NAME in file.php on line 1269 Warning: Undefined index: DESC in file.php on line 1270 Warning: Undefined variable: $str in a.php on line 341 Warning: Undefined index: DESC in file.php on line 1270 @ Error suppression is bad http://derickrethans.nl/five-reasons-why-the-shutop-operator-should-be-avoided.html 40
  • 77. Watch your error log! Warning: Undefined index: NAME in file.php on line 1269 Warning: Undefined index: DESC in file.php on line 1270 Warning: Undefined variable: $str in a.php on line 341 Warning: Undefined index: DESC in file.php on line 1270 @ Error suppression is bad http://derickrethans.nl/five-reasons-why-the-shutop-operator-should-be-avoided.html 40
  • 78. Log and analyse external requests /* HTTP Client */ function request($url, $method = ‘GET’) { $this->log(‘[HTTP] ’ . $method . ‘ ’ . $url); $ch = curl_init(); // ... } /* DB Client */ function query($sql) { $this->log(‘[DB] ’ . $query); // ... return $this->db->query($sql); } 41
  • 79. Log and analyse external requests /* HTTP Client */ function request($url, $method = ‘GET’) { $this->log(‘[HTTP] ’ . $method . ‘ ’ . $url); $ch = curl_init(); // ... } /* DB Client */ function query($sql) { $this->log(‘[DB] ’ . $query); // ... return $this->db->query($sql); } 41
  • 80. Analyse logs (Observability) [HTTP] GET http://myservice.com/list/somelist [HTTP] GET http://myservice.com/id/1 [HTTP] PUT http://myservice.com/id/3/value/xyz [HTTP] GET http://myservice.com/list/somelist [HTTP] GET http://myservice.com/list/somelist [DB] SELECT * FROM mytable; [DB] SELECT name FROM cities ORDER BY name; [DB] SELECT name FROM cities ORDER BY name; [DB] SELECT name FROM cities ORDER BY name; [DB] UPDATE mytable SET name=‘xyz’ WHERE id=3; Consider caching frequent requests 42
  • 81. Profile under load Collect profile data from a sample of random requests if (0 == mt_rand(0, 1000)) { // collect profile data } 43
  • 82. Profile under load Collect profile data from a sample of random requests if (0 == mt_rand(0, 1000)) { // collect profile data } Monitor resource usage in real time (top / htop / vmstat) 43
  • 83. Profile under load Collect profile data from a sample of random requests if (0 == mt_rand(0, 1000)) { // collect profile data } Monitor resource usage in real time (top / htop / vmstat) Analyse graphs JMeter The Grinder 43
  • 84. Performance Counters Used / available memory (local, shared) Pages / sec Response time & Time-outs Successful / failed transactions Open sockets / connections Bandwidth (internal network too!) CPU All the resources are limited ... 44
  • 85. Thundering Herd 45
  • 86. Circuit Breaker 46
  • 87. Circuit Breaker public function fetchData($key) { return $this->callService($key); } 47
  • 88. Circuit Breaker public function fetchData($key) { $config = array( 'check_timeout' => 10, 'failure_threshold' => 10, ); $cb = new Circuit_Breaker('name', $config); if ($cb->isClosed()) { $ok = $this->callService($key); public function fetchData($key) { if ($ok) { return $this->callService($key); } $cb->success(); } else { //increment failures count $cb->fail(); } } } 47
  • 89. Poor man’s circuit breaker public function fetchData($key) { static $failures = 0; if ($failures < self::MAX_FAILURES) { $ok = $this->callService($key); if ($ok) { --$failures; } else { ++$failures; } } } Save the counter in memcached instead of a static variable, if possible 48
  • 90. Reverse Proxy http://www.varnish-cache.org/ 49
  • 91. Conclusions Parting thoughts 50
  • 92. Performance Checklist Deep understanding of the problem at hand Good architecture / design / data structures / algorithms Do the absolute minimum Use the right tools, always validate assumptions Monitor resource usage (!), with many metrics Spread the load uniformly across the resources Decouple services Good caching strategy strategies (sharing / reuse) 51
  • 93. Links + References http://www.slideshare.net/postwait/scalable-internet-architecture http://techportal.ibuildings.com/2009/12/01/profiling-with-xhprof/ http://www.chehodgins.com/php/sorting-out-your-php-includes-using-inclued/ http://weierophinney.net/matthew/archives/245-Autoloading-Benchmarks.html http://www.mail-archive.com/internals@lists.php.net/msg37192.html http://talks.php.net/show/digg http://mirror.facebook.net/facebook/xhprof/ http://xdebug.org/ http://github.com/facebook/pfff/ 52
  • 94. Image Credits http://michaelscomments.files.wordpress.com/2009/10/onion- centurion.jpg http://www.socketmanufacturers.com/miniature-circuit- breaker/DZ47-63-3P-Miniature-Circuit-Breaker.jpg http://onscreencars.com/tv/the-homer-the-car-built-for-homer/ http://etc.usf.edu/presentations/themes/index.html http://www.iconfinder.com/ 53
  • 95. Questions? 54
  • 96. Lorenzo Alberton @lorenzoalberton Thank you! lorenzo@alberton.info http://www.alberton.info/talks http://joind.in/talk/view/2051 55