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.

Links

Advertisements

23 thoughts on “Zend Framework Controller: 22% Drop in Responsiveness

  1. Pingback: Oxeron Internet and Mobile Service Sarl

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

  3. 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?

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

  5. 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…

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

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

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

  9. 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. :-))

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

  11. Pingback: PHPUnit: Testing Zend Framework Controllers

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

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

  14. I don’t understand. You are executing a php script with Zend Studio? How, where and what are you benchmarking?

  15. Pingback: Zend Framework Automatic Dependency Tracking

  16. Pingback: Implementing your own Front Controller in Zend Framework « fede.carg ( blog )

  17. Pingback: Zend Framework und Performance « Net-Entwicklung.de - Mein Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s