Profile your PHP application and make it fly

47,441 views
47,456 views

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

Published in: Technology
2 Comments
57 Likes
Statistics
Notes
  • 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 :-)
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Thanks A Lot. Loved it.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
47,441
On SlideShare
0
From Embeds
0
Number of Embeds
7,701
Actions
Shares
0
Downloads
0
Comments
2
Likes
57
Embeds 0
No embeds

No notes for slide
































































































  • Profile your PHP application and make it fly

    1. 1. Lorenzo Alberton Profile your PHP application and make it fly The path to performance and scalability PHPNW, Saturday 9th October 2010 1
    2. 2. Quiz time! Which is faster? 2
    3. 3. Quiz time! Which is faster? (A) echo (B) print 2
    4. 4. Quiz time! Which is faster? (A) echo single (B) print double quotes quotes 2
    5. 5. Answer It probably doesn’t matter. 3
    6. 6. Awareness is the Know your targets Know what it will take to meet them Know your constraints 4
    7. 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. 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. 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. 10. XHProf UI Report - Web interface 8
    11. 11. XHProf UI Report - Web interface 8
    12. 12. XHProf UI Report - Web interface 8
    13. 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. 14. XHProf UI - Trace calls 9
    15. 15. XHProf UI - Call Graph 10
    16. 16. XHProf UI - Call Graph 10
    17. 17. Measuring Analyse the framework Analyse your application 11
    18. 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. 19. Zend Framework Baseline $ zf.sh create project testzf 13
    20. 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. 21. Shortcuts - The Real Cost public function doSomething(array $data) { // Zend_Config object $config = $this->getConfig(); $var = $config->aaa->bbb->ccc; // ... } 15
    22. 22. Shortcuts - The Real Cost public function doSomething(array $data) { // Zend_Config object $config = $this->getConfig(); $var = $config->aaa->bbb->ccc; // ... } 15
    23. 23. Shortcuts - The Real Cost public function doSomething(array $data) { // Zend_Config object $config = $this->getConfig(); $var = $config->aaa->bbb->ccc; // ... } 15
    24. 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. 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. 26. partial() vs. render() <!-- mytemplate.phtml --> <ul> <?php foreach ($this->rows as $row) { $this->partial(‘listItemTemplate.phtml’, ‘mymodule’, array(‘data’ => $row); } ?> </ul> 17
    27. 27. partial() vs. render() 18
    28. 28. partial() vs. render() 18
    29. 29. partial() vs. render() 18
    30. 30. partial() vs. render() 18
    31. 31. partial() vs. render() 18
    32. 32. partial() vs. render() 18
    33. 33. partial() vs. render() 18
    34. 34. partial() vs. render() <!-- mytemplate.phtml --> <ul> <?php foreach ($this->rows as $row) { $this->partial(‘listItemTemplate.phtml’, ‘mymodule’, array(‘data’ => $row); } ?> </ul> 19
    35. 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. 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. 37. Plugins / Helpers <!-- mytemplate.phtml --> <ul> <?php foreach ($this->rows as $row) { echo $this->myViewHelper($row); } ?> </ul> 21
    38. 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. 39. Plugins / Helpers <!-- mytemplate.phtml --> <ul> <?php foreach ($this->rows as $row) { echo $this->myViewHelper($row); } ?> </ul> 21
    40. 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. 41. Know Your Application Every feature has a cost. Measure it. 22
    42. 42. Optimisation Techniques Tips and tricks to get faster applications 23
    43. 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. 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. 45. Profiling as Design validation practice 25
    46. 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. 47. Refactoring hints PREPARE SCREEN showing db calls 27
    48. 48. Refactoring hints PREPARE SCREEN showing db calls 27
    49. 49. Refactoring hints 28
    50. 50. Refactoring hints 28
    51. 51. Refactoring hints 28
    52. 52. Refactoring hints 29
    53. 53. Refactoring hints PHP is FAST 29
    54. 54. Refactoring hints PHP is FAST ...but do you really need to call strtolower() 15000 times? 29
    55. 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. 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. 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. 58. Caching (computational reuse) Static variables APC / Zend Server Cache / ... Memcached Reverse proxies Browser 31
    59. 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. 60. [Cache] Layers! 32
    61. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 72. OPCode cache APC XCache Zend Accelerator WinCache eAccelerator ... 38
    73. 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. 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. 75. Include Hierarchy - PECL inclued http://pecl.php.net/package/inclued 39
    76. 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. 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. 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. 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. 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. 81. Profile under load Collect profile data from a sample of random requests if (0 == mt_rand(0, 1000)) { // collect profile data } 43
    82. 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. 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. 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. 85. Thundering Herd 45
    86. 86. Circuit Breaker 46
    87. 87. Circuit Breaker public function fetchData($key) { return $this->callService($key); } 47
    88. 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. 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. 90. Reverse Proxy http://www.varnish-cache.org/ 49
    91. 91. Conclusions Parting thoughts 50
    92. 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. 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. 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. 95. Questions? 54
    96. 96. Lorenzo Alberton @lorenzoalberton Thank you! lorenzo@alberton.info http://www.alberton.info/talks http://joind.in/talk/view/2051 55

    ×