Profile your PHP application and make it fly

Lorenzo Alberton
Lorenzo AlbertonCTO at DataSift, Inc.
Lorenzo Alberton


Profile your PHP application
             and make it fly
               The path to performance
                          and scalability




             PHPNW, Saturday 9th October 2010
                                                1
Quiz time!

             Which is faster?




                                2
Quiz time!

             Which is faster?

  (A) echo               (B) print




                                     2
Quiz time!

             Which is faster?

  (A) echo
      single             (B) print
                             double
      quotes                 quotes




                                      2
Answer



         It probably doesn’t matter.




                                       3
Awareness is the




  Know your targets
  Know what it will take to meet them
  Know your constraints


                                        4
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
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
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
XHProf UI Report - Web interface




                                   8
XHProf UI Report - Web interface




                                   8
XHProf UI Report - Web interface




                                   8
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
XHProf UI - Trace calls




                          9
XHProf UI - Call Graph




                         10
XHProf UI - Call Graph




                         10
Measuring
 Analyse the framework
Analyse your application




                           11
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
Zend Framework                 Baseline




     $ zf.sh create project testzf
                                          13
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
Shortcuts - The Real Cost
 public function doSomething(array $data) {
    // Zend_Config object
    $config = $this->getConfig();
    $var = $config->aaa->bbb->ccc;
    // ...
 }




                                              15
Shortcuts - The Real Cost
 public function doSomething(array $data) {
    // Zend_Config object
    $config = $this->getConfig();
    $var = $config->aaa->bbb->ccc;
    // ...
 }




                                              15
Shortcuts - The Real Cost
 public function doSomething(array $data) {
    // Zend_Config object
    $config = $this->getConfig();
    $var = $config->aaa->bbb->ccc;
    // ...
 }




                                              15
Shortcuts - The Real Cost
 public function doSomething(array $data) {
    // Zend_Config object
    $config = $this->getConfig();
    $var = $config->aaa->bbb->ccc;
    // ...
 }



      2

      1
      3
                                              15
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
partial() vs. render()


   <!-- mytemplate.phtml -->
   <ul>
       <?php
       foreach ($this->rows as $row) {
          $this->partial(‘listItemTemplate.phtml’,
                ‘mymodule’, array(‘data’ => $row);
       }
       ?>
   </ul>



                                                     17
partial() vs. render()




                         18
partial() vs. render()




                         18
partial() vs. render()




                         18
partial() vs. render()




                         18
partial() vs. render()




                         18
partial() vs. render()




                         18
partial() vs. render()




                         18
partial() vs. render()


   <!-- mytemplate.phtml -->
   <ul>
       <?php
       foreach ($this->rows as $row) {
          $this->partial(‘listItemTemplate.phtml’,
                ‘mymodule’, array(‘data’ => $row);
       }
       ?>
   </ul>



                                                     19
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
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
Plugins / Helpers

  <!-- mytemplate.phtml -->
  <ul>
      <?php

     foreach ($this->rows as $row) {
        echo $this->myViewHelper($row);
     }

      ?>
  </ul>




                                          21
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
Plugins / Helpers

  <!-- mytemplate.phtml -->
  <ul>
      <?php

     foreach ($this->rows as $row) {
        echo $this->myViewHelper($row);
     }

      ?>
  </ul>




                                          21
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
Know Your Application




       Every feature has a cost. Measure it.
                                               22
Optimisation Techniques
 Tips and tricks to get faster applications




                                              23
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
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
Profiling as Design validation practice




                                     25
Easy performance targets

 External resources (http, db, disk I/O)
 Memory-hungry function
 (xml/json parsers, output buffering)
 Loops
 Regular expressions
 Wrappers




                                           26
Refactoring hints


               PREPARE SCREEN
               showing db calls




                                  27
Refactoring hints


               PREPARE SCREEN
               showing db calls




                                  27
Refactoring hints




                    28
Refactoring hints




                    28
Refactoring hints




                    28
Refactoring hints




                    29
Refactoring hints

  PHP is FAST




                    29
Refactoring hints

  PHP is FAST

  ...but do you really need to call strtolower()
      15000 times?




                                                   29
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
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
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
Caching (computational reuse)
 Static variables
 APC / Zend Server Cache / ...
 Memcached
 Reverse proxies
 Browser




                                 31
Caching (computational reuse)
 Static variables
 APC / Zend Server Cache / ...
 Memcached
 Reverse proxies
 Browser



             Do no work at all, if you can avoid it


                                                      31
[Cache] Layers!




                  32
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
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
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
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
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
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
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
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
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
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
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
OPCode cache
          APC                XCache
          Zend Accelerator   WinCache
          eAccelerator       ...




                                        38
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
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
Include Hierarchy - PECL inclued




http://pecl.php.net/package/inclued
                                      39
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
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
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
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
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
Profile under load
 Collect profile data from a sample of random requests
      if (0 == mt_rand(0, 1000)) {
          // collect profile data
      }




                                                        43
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
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
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
Thundering Herd




                  45
Circuit Breaker




                  46
Circuit Breaker




   public function fetchData($key) {
       return $this->callService($key);
   }




                                          47
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
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
Reverse Proxy




        http://www.varnish-cache.org/



                                        49
Conclusions
    Parting thoughts




                       50
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
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
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
Questions?




             54
Lorenzo Alberton
                   @lorenzoalberton




   Thank you!
       lorenzo@alberton.info
http://www.alberton.info/talks




   http://joind.in/talk/view/2051
                                      55
1 of 96

Recommended

Modern Algorithms and Data Structures - 1. Bloom Filters, Merkle Trees by
Modern Algorithms and Data Structures - 1. Bloom Filters, Merkle TreesModern Algorithms and Data Structures - 1. Bloom Filters, Merkle Trees
Modern Algorithms and Data Structures - 1. Bloom Filters, Merkle TreesLorenzo Alberton
30K views49 slides
Monitoring at scale - Intuitive dashboard design by
Monitoring at scale - Intuitive dashboard designMonitoring at scale - Intuitive dashboard design
Monitoring at scale - Intuitive dashboard designLorenzo Alberton
126.8K views155 slides
Scalable Architectures - Taming the Twitter Firehose by
Scalable Architectures - Taming the Twitter FirehoseScalable Architectures - Taming the Twitter Firehose
Scalable Architectures - Taming the Twitter FirehoseLorenzo Alberton
50.2K views97 slides
The Art of Scalability - Managing growth by
The Art of Scalability - Managing growthThe Art of Scalability - Managing growth
The Art of Scalability - Managing growthLorenzo Alberton
87.5K views123 slides
Scaling Teams, Processes and Architectures by
Scaling Teams, Processes and ArchitecturesScaling Teams, Processes and Architectures
Scaling Teams, Processes and ArchitecturesLorenzo Alberton
41.2K views66 slides
Graphs in the Database: Rdbms In The Social Networks Age by
Graphs in the Database: Rdbms In The Social Networks AgeGraphs in the Database: Rdbms In The Social Networks Age
Graphs in the Database: Rdbms In The Social Networks AgeLorenzo Alberton
118.1K views161 slides

More Related Content

Recently uploaded

Microchip: CXL Use Cases and Enabling Ecosystem by
Microchip: CXL Use Cases and Enabling EcosystemMicrochip: CXL Use Cases and Enabling Ecosystem
Microchip: CXL Use Cases and Enabling EcosystemCXL Forum
129 views12 slides
Web Dev - 1 PPT.pdf by
Web Dev - 1 PPT.pdfWeb Dev - 1 PPT.pdf
Web Dev - 1 PPT.pdfgdsczhcet
52 views45 slides
The Research Portal of Catalonia: Growing more (information) & more (services) by
The Research Portal of Catalonia: Growing more (information) & more (services)The Research Portal of Catalonia: Growing more (information) & more (services)
The Research Portal of Catalonia: Growing more (information) & more (services)CSUC - Consorci de Serveis Universitaris de Catalunya
66 views25 slides
MemVerge: Past Present and Future of CXL by
MemVerge: Past Present and Future of CXLMemVerge: Past Present and Future of CXL
MemVerge: Past Present and Future of CXLCXL Forum
110 views26 slides
Combining Orchestration and Choreography for a Clean Architecture by
Combining Orchestration and Choreography for a Clean ArchitectureCombining Orchestration and Choreography for a Clean Architecture
Combining Orchestration and Choreography for a Clean ArchitectureThomasHeinrichs1
68 views24 slides
Business Analyst Series 2023 - Week 3 Session 5 by
Business Analyst Series 2023 -  Week 3 Session 5Business Analyst Series 2023 -  Week 3 Session 5
Business Analyst Series 2023 - Week 3 Session 5DianaGray10
165 views20 slides

Recently uploaded(20)

Microchip: CXL Use Cases and Enabling Ecosystem by CXL Forum
Microchip: CXL Use Cases and Enabling EcosystemMicrochip: CXL Use Cases and Enabling Ecosystem
Microchip: CXL Use Cases and Enabling Ecosystem
CXL Forum129 views
Web Dev - 1 PPT.pdf by gdsczhcet
Web Dev - 1 PPT.pdfWeb Dev - 1 PPT.pdf
Web Dev - 1 PPT.pdf
gdsczhcet52 views
MemVerge: Past Present and Future of CXL by CXL Forum
MemVerge: Past Present and Future of CXLMemVerge: Past Present and Future of CXL
MemVerge: Past Present and Future of CXL
CXL Forum110 views
Combining Orchestration and Choreography for a Clean Architecture by ThomasHeinrichs1
Combining Orchestration and Choreography for a Clean ArchitectureCombining Orchestration and Choreography for a Clean Architecture
Combining Orchestration and Choreography for a Clean Architecture
ThomasHeinrichs168 views
Business Analyst Series 2023 - Week 3 Session 5 by DianaGray10
Business Analyst Series 2023 -  Week 3 Session 5Business Analyst Series 2023 -  Week 3 Session 5
Business Analyst Series 2023 - Week 3 Session 5
DianaGray10165 views
.conf Go 2023 - How KPN drives Customer Satisfaction on IPTV by Splunk
.conf Go 2023 - How KPN drives Customer Satisfaction on IPTV.conf Go 2023 - How KPN drives Customer Satisfaction on IPTV
.conf Go 2023 - How KPN drives Customer Satisfaction on IPTV
Splunk86 views
Transcript: The Details of Description Techniques tips and tangents on altern... by BookNet Canada
Transcript: The Details of Description Techniques tips and tangents on altern...Transcript: The Details of Description Techniques tips and tangents on altern...
Transcript: The Details of Description Techniques tips and tangents on altern...
BookNet Canada119 views
How to reduce cold starts for Java Serverless applications in AWS at JCON Wor... by Vadym Kazulkin
How to reduce cold starts for Java Serverless applications in AWS at JCON Wor...How to reduce cold starts for Java Serverless applications in AWS at JCON Wor...
How to reduce cold starts for Java Serverless applications in AWS at JCON Wor...
Vadym Kazulkin70 views
[2023] Putting the R! in R&D.pdf by Eleanor McHugh
[2023] Putting the R! in R&D.pdf[2023] Putting the R! in R&D.pdf
[2023] Putting the R! in R&D.pdf
Eleanor McHugh38 views
Empathic Computing: Delivering the Potential of the Metaverse by Mark Billinghurst
Empathic Computing: Delivering  the Potential of the MetaverseEmpathic Computing: Delivering  the Potential of the Metaverse
Empathic Computing: Delivering the Potential of the Metaverse
Mark Billinghurst449 views
MemVerge: Memory Viewer Software by CXL Forum
MemVerge: Memory Viewer SoftwareMemVerge: Memory Viewer Software
MemVerge: Memory Viewer Software
CXL Forum118 views
Future of Learning - Khoong Chan Meng by NUS-ISS
Future of Learning - Khoong Chan MengFuture of Learning - Khoong Chan Meng
Future of Learning - Khoong Chan Meng
NUS-ISS31 views
"AI Startup Growth from Idea to 1M ARR", Oleksandr Uspenskyi by Fwdays
"AI Startup Growth from Idea to 1M ARR", Oleksandr Uspenskyi"AI Startup Growth from Idea to 1M ARR", Oleksandr Uspenskyi
"AI Startup Growth from Idea to 1M ARR", Oleksandr Uspenskyi
Fwdays26 views
TE Connectivity: Card Edge Interconnects by CXL Forum
TE Connectivity: Card Edge InterconnectsTE Connectivity: Card Edge Interconnects
TE Connectivity: Card Edge Interconnects
CXL Forum96 views
Beyond the Hype: What Generative AI Means for the Future of Work - Damien Cum... by NUS-ISS
Beyond the Hype: What Generative AI Means for the Future of Work - Damien Cum...Beyond the Hype: What Generative AI Means for the Future of Work - Damien Cum...
Beyond the Hype: What Generative AI Means for the Future of Work - Damien Cum...
NUS-ISS28 views
AMD: 4th Generation EPYC CXL Demo by CXL Forum
AMD: 4th Generation EPYC CXL DemoAMD: 4th Generation EPYC CXL Demo
AMD: 4th Generation EPYC CXL Demo
CXL Forum126 views

Featured

ChatGPT and the Future of Work - Clark Boyd by
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
20.3K views69 slides
Getting into the tech field. what next by
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
5K views22 slides
Google's Just Not That Into You: Understanding Core Updates & Search Intent by
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
5.8K views99 slides
How to have difficult conversations by
How to have difficult conversations How to have difficult conversations
How to have difficult conversations Rajiv Jayarajah, MAppComm, ACC
4.3K views19 slides
Introduction to Data Science by
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data ScienceChristy Abraham Joy
82.1K views51 slides
Time Management & Productivity - Best Practices by
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
169.6K views42 slides

Featured(20)

ChatGPT and the Future of Work - Clark Boyd by Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
Clark Boyd20.3K views
Getting into the tech field. what next by Tessa Mero
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
Tessa Mero5K views
Google's Just Not That Into You: Understanding Core Updates & Search Intent by Lily Ray
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Lily Ray5.8K views
Time Management & Productivity - Best Practices by Vit Horky
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
Vit Horky169.6K views
The six step guide to practical project management by MindGenius
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
MindGenius36.6K views
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright... by RachelPearson36
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
RachelPearson3612.6K views
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present... by Applitools
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Applitools55.4K views
12 Ways to Increase Your Influence at Work by GetSmarter
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work
GetSmarter401.6K views
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G... by DevGAMM Conference
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
DevGAMM Conference3.6K views
Barbie - Brand Strategy Presentation by Erica Santiago
Barbie - Brand Strategy PresentationBarbie - Brand Strategy Presentation
Barbie - Brand Strategy Presentation
Erica Santiago25.1K views
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them well by Saba Software
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them wellGood Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Saba Software25.2K views
Introduction to C Programming Language by Simplilearn
Introduction to C Programming LanguageIntroduction to C Programming Language
Introduction to C Programming Language
Simplilearn8.4K views
The Pixar Way: 37 Quotes on Developing and Maintaining a Creative Company (fr... by Palo Alto Software
The Pixar Way: 37 Quotes on Developing and Maintaining a Creative Company (fr...The Pixar Way: 37 Quotes on Developing and Maintaining a Creative Company (fr...
The Pixar Way: 37 Quotes on Developing and Maintaining a Creative Company (fr...
Palo Alto Software88.3K views
9 Tips for a Work-free Vacation by Weekdone.com
9 Tips for a Work-free Vacation9 Tips for a Work-free Vacation
9 Tips for a Work-free Vacation
Weekdone.com7.2K views
How to Map Your Future by SlideShop.com
How to Map Your FutureHow to Map Your Future
How to Map Your Future
SlideShop.com275.1K views

Profile your PHP application and make it fly

  • 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
  • 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
  • 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
  • 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
  • 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
  • 96. Lorenzo Alberton @lorenzoalberton Thank you! lorenzo@alberton.info http://www.alberton.info/talks http://joind.in/talk/view/2051 55

Editor's Notes