The most important factor in making a Web application fast is its basic design. You must also know what kinds of processing your framework is doing, and what its bottlenecks are. The best way to find the performance bottlenecks is to monitor the performance counters and to have a thorough understanding of the framework your application is using.
Paul M. Jones spotted something interesting a couple of weeks ago:
"The difference between the 1.0 release and the 1.5 release of the Zend Framework is quite dramatic: a 25% drop in responsiveness. And then another 10% drop between 1.5 and 1.6."
According to Paul, the Zend Framework lost 35% of their requests per second between 1.0 and 1.6 releases. This means that a Web server will serve 65 instead of 100 requests per second.
You should definitely benchmark your application to find out where the bottlenecks are. Even if the overall performance for your application is currently acceptable, you should at least make a plan for each bottleneck and decide how to solve it if someday you really need the extra performance.
After spending a couple of hours playing around with the Zend Framework, I found out that the Zend_Controller_Action_Helper_ViewRenderer component, introduced in May 2007, is the main cause of performance degradation. It currently loads and uses the following classes:
Zend/Filter.php Zend/Filter/Interface.php Zend/Filter/Inflector.php Zend/Filter/PregReplace.php Zend/Filter/Word/Separator/Abstract.php Zend/Filter/Word/SeparatorToSeparator.php Zend/Filter/Word/UnderscoreToSeparator.php Zend/Filter/Word/CamelCaseToSeparator.php Zend/Filter/Word/CamelCaseToDash.php Zend/Filter/StringToLower.php Zend/Loader/PluginLoader.php Zend/Loader/PluginLoader/Interface.php
To test this, I created an Action Controller that uses the Zend_Controller_Action_Helper_ViewRenderer helper class:
class Blog_IndexController extends Zend_Controller_Action { public function indexAction() { $this->view->message = 'indexAction'; } }
Benchmark:
$ ab -kc 80 -t 60 http://www.zend-test.com/blog/2008/07/14/test
Test result data:
Concurrency Level: 80 Time taken for tests: 60.33175 seconds Complete requests: 5828 Failed requests: 0 Write errors: 0 Keep-Alive requests: 5790 Total transferred: 2457771 bytes HTML transferred: 1270504 bytes Requests per second: 97.08 [#/sec] (mean) Time per request: 824.066 [ms] (mean) Time per request: 10.301 [ms] Transfer rate: 39.98 [Kbytes/sec] received
Rasmus gave an excellent presentation about performance at DrupalCon, where he said: "You need to ask yourself: Does my application need all this classes? If it doesn't, get rid of them".
And so I did. I removed the Zend_Controller_Action_Helper_ViewRenderer instance from the Front Controller (line 831):
Zend_Controller_Action_HelperBroker::getStack()->offsetSet(-80, new Zend_Controller_Action_Helper_ViewRenderer());
And instantiated the view object within the Blog Controller:
class Blog_IndexController extends Zend_Controller_Action { public function indexAction() { $this->initView(); $this->view->message = 'indexAction'; this->render(); } }
Benchmark:
$ ab -kc 80 -t 60 http://www.zend-test.com/blog/2008/07/14/test
Test result data:
Concurrency Level: 80 Time taken for tests: 60.3663 seconds Complete requests: 7472 Failed requests: 0 Write errors: 0 Keep-Alive requests: 7435 Total transferred: 3061944 bytes HTML transferred: 1539232 bytes Requests per second: 124.53 [#/sec] (mean) Time per request: 642.438 [ms] (mean) Time per request: 8.030 [ms] Transfer rate: 49.83 [Kbytes/sec] received
As you can see, the output is the same, but the response time is lower:
((124 - 97 ) ÷ 124) × 100 = 22%
Even with best planning, you may still have to investigate performance problems during web development. The process of identifying and fixing bottlenecks should be done in a serial manner. Vary only one line of code at a time and then measure performance to verify the impact of the single change. Also, to avoid problems like this, you should put some effort into benchmarking your whole application under the worst possible load.