Zend Framework Controller: 22% Drop in Responsiveness
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.
Finding Performance Bottlenecks
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%
Conclusion
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.
Zend welcomes and encourages contributions to the Zend Framework community. Anyone may report issues or feature requests and ask questions and/or provide answers on the mailing lists. Zend also encourages you to publish articles or code on your own blog or website. Find out more.
[...] ? Voici un exemple avec ZendFramework trouvé ici : Paul M. Jones spotted something interesting a couple of weeks ago: “The difference between [...]
Oxeron Internet and Mobile Service Sarl
September 17, 2008 at 6:04 am
You mean 37.5%? (wink)
Matt
September 17, 2008 at 10:56 pm
You don’t have to physically remove/edit the Zend/Controller/Front.php file to disable the view helper. You can simply place this in your bootstrap before you dispatch your front controller:
$front->setParam(‘noViewRenderer’, true);
This will disable the installation of the default viewRenderer helper.
Good find on this, I’m pulling it out of my code right now and switching to on-demand view loading.
-Jeff
Jeff
September 18, 2008 at 1:04 am
Forgive me if you’ve already thought of this or I’m confused, but couldn’t you extend Zend_Controller_Front, reimplement dispatch and avoid hacking the core?
Scott
September 18, 2008 at 6:38 am
You can disable the helper class (as explained above).
Federico
September 18, 2008 at 8:41 am
It seems you can extend the frontcontroller, if needed
http://framework.zend.com/manual…
Ludo
September 18, 2008 at 11:31 am
Overriding the getInstance() method does not ensure that calls to Zend_Controller_Front::getInstance() will return an instance of your new subclass, and therefore the documentation is incomplete (or wrong). For example: http://phpimpact.codepad.org/bKJR78qP. Outputs: Zend_Controller_Front instead of My_Controller_Front
Federico
September 18, 2008 at 11:57 am
Indeed, and it seems pretty clear that calling Zend_Controller_Front::getInstance() will not return a instance of the subclassing class…
One would call directly
My_Controller_Front::getInstance(), but the Zend_Controller_Front::getInstance() is used many times in the main classes…
Ludo
September 18, 2008 at 12:25 pm
But, addition to my prev comment, calling at first
My_Controller_Front::getInstance(), all the subsequent calls of Zend_Controller_Front::getInstance() WILL return a My_Controller_Front instance.
Ludo
September 18, 2008 at 12:45 pm
If your main concern is performance, then extending the front controller only makes it worst. Also, extending the front controller and overwriting the getInstance() method creates inconsistency. You are setting an instance of the subclass using the My_Controller_Front::getInstance() method, while other components are retrieving it using the Zend_Controller_Front::getInstance() method. In my opinion it’s responsibility of the front controller to expose a setter method to set new instances, similar to what Zend_Registry does. For example:
http://phpimpact.codepad.org/zQyN1TfQ
Anyway, in this particular case, disabling the ViewRenderer helper class does the job.
Federico
September 19, 2008 at 9:38 am
Although the discussion about the front controller is irrelevant to this post, I think it’s important to note that if you do extend the front controller, you need to set the instance of the controller before instantiating any other class, otherwise, it will return an instance of the parent class.
Federico
September 20, 2008 at 5:27 pm
Is it possible to use Zend_Layout with this setup?
gog
October 11, 2008 at 1:33 pm
Yes sure :)
Federico
October 13, 2008 at 5:09 pm
Interesting idea, I didn’t benchmark with ab yet, just Xdebug so far and I couldn’t tell any larger difference, in fact the app took longer to load on various tries (not just on the first hit).
Any ideas?
(P.S. Ping me via email if you reply here, I am not sure if your blog “subscribes” me to this entry. :-))
till
November 1, 2008 at 8:51 pm
I tried $front->setParam(‘noViewRenderer’, true) but app seemed to get stuck in a redirect loop – any idea why that might be? Many thanks for your blog and activity in ZF community
Mothmenace
November 24, 2008 at 5:27 pm
[...] Please note that disabling the ViewRenderer helper is optional. However, you should know that the use of Zend_Controller_Action_Helper_ViewRenderer class can result in performance degradation. More info here. [...]
PHPUnit: Testing Zend Framework Controllers
December 27, 2008 at 10:56 am
I tested it with ZF 1.7.2, and i only notice ~2% drop in responsiveness. Turning on APC increase this drop to ~5%, but ‘requests per second’ is much bigger.
Raven
January 3, 2009 at 1:49 pm
I suggest you publish your test results, like Paul Jones did, so we can analyse them.
Federico
January 3, 2009 at 1:53 pm
It was simple start application from recent Zend Studio for Eclipse, witch use Zend_Layout. I disabled View helper by $front->setParam(’noViewRenderer’, true), but disabling it just like in this article produce the same results.
Raven
January 5, 2009 at 6:59 pm
I don’t understand. You are executing a php script with Zend Studio? How, where and what are you benchmarking?
Federico
January 5, 2009 at 7:09 pm
[...] it can also be used to evaluate packages. Sometimes packages create unnecessary dependencies, Zend_Loader is a good example of [...]
Zend Framework Automatic Dependency Tracking
February 2, 2009 at 12:18 pm
[...] the default Front Controller? What if you don’t need all that flexibility, a URL mapper, a ViewRendered plugin or an ActionStack helper. Maybe all you need is just a couple of controllers, and [...]
Implementing your own Front Controller in Zend Framework « fede.carg ( blog )
April 5, 2009 at 12:12 pm
[...] Hier gibt es ein interessanter Artikel über Performance Bottlenecks. [...]
Zend Framework und Performance « Net-Entwicklung.de - Mein Blog
April 25, 2009 at 11:20 pm