Federico Cargnelutti

Simple is better than complex. Complex is better than complicated. | @fedecarg

Zend_Form Performance Issues

with 10 comments

If you are using Zend_Form and your site increases in complexity and attracts more traffic, you are most likely to run into performance problems.

Of course, bigger and more complex projects result in more load on your servers. A typical scenario might be you’ve added a few new features to a Web application and now are seeing more server load and memory usage; thus, pages seem to load slower. To solve this you can throw bigger hardware at the problem or try to find the parts of your code that are causing the slowdowns and optimize them.

In this post I’ll explain the problems I faced when using Zend_Form and how I managed to optimize it and improve the performance of my application.

Update: Matthew Weier O’Phinney wrote:

“You’ll be happy to know that I’ve already made changes in the incubator to allow lazy loading of validators, filters, and decorators. These changes will be released with version 1.6.0.”

The Problem

Zend_Form heavily depends on other Zend Framework components, such as Zend_Validate, Zend_Filter, Zend_Loader_PluginLoader and, optionally, Zend_Config.

The Zend_Form_Elements class doesn’t check for the requested method and loads all the filters and validators on each request creating an unnecessary overhead. In an ideal world, these objects should only be loaded when the elements are checked against the data provided.

1. HTTP/1.1 GET

o- (request)
-> (new form)
-> (load filters and validators)
-> (response)

2. HTTP/1.1 POST

o- (request)
-> (new form)
-> (load filters and validators)
-> (process data)
-> (response)

In the first example, when a user requests the form, Zend_Form loads all the filters and validators, but never uses them.

The Test

Zend_Form

Requests per second 1:   36.51 [#/sec]
Requests per second 2:   36.72 [#/sec]
Requests per second 3:   36.54 [#/sec]

Zend_Form + Zend_Cache

Requests per second 1:   81.54 [#/sec]
Requests per second 2:   81.38 [#/sec]
Requests per second 3:   82.01 [#/sec]

HTML Form

Requests per second 1:   94.55 [#/sec]
Requests per second 2:   94.63 [#/sec]
Requests per second 3:   94.49 [#/sec]

Benchmarks and Test Data Results

The Solution

1. Modify Zend_Form_Elements so that it checks the requested method before loading all the dependencies, otherwise Zend_Form_Elements will use Reflection to allocate unnecessary objects into memory.

2. Cache the output of the Zend_Form object using Zend_Cache.

Example

Caching Zend_Form output:

class ZendFormCachedController extends Zend_Controller_Action
{
        protected $_formId = 'form';

        public function indexAction()
        {
                $frontend = array(
                        'lifetime' => 7200,
                        'automatic_serialization' => true);

                $backend = array('cache_dir' => '/tmp/');
                $cache = Zend_Cache::factory('Core', 'File', $frontend, $backend);

                if ($this->getRequest()->isPost()) {
                        $form = $this->getForm(new Zend_Form);
                } else if (! $form = $cache->load($this->_formId)) {
                        $form = $this->getForm(new Zend_Form);
                        $cache->save($form, $this->_formId);
                }

                $this->getHelper('layout')->setLayout('zend-form');
                $this->view->form = $form;
        }

Conclusion

Make sure you cache and optimize your code! When you add a new component to your application, make sure you have a benchmark against which you can test your code to see how the changes affect performance and efficiency.

Zend_Form Benchmarks and Test Data Results

Related Articles

PHP framework comparison benchmarks

Written by Federico

July 6, 2008 at 4:49 pm

Posted in Frameworks, PHP

10 Responses

Subscribe to comments with RSS.

  1. You’ll be happy to know that I’ve already made changes in the incubator to allow lazy loading of validators, filters, and decorators; basically, the plugin objects are only instantiated when you actually need them (for instance, validators are only loaded when you either call getValidator() or isValid(); filters are only loaded when you call getFilter() or getValue(); and decorators are only rendered if you call getDecorator() or render()). These changes will be released with version 1.6.0.

  2. That was quick, thanks! (post updated)

    phpimpact

    July 7, 2008 at 5:42 pm

  3. [...] Zend_Form Performance Issues – для любителей фреймворка от компании Zend интересное исследование производительности и вообще нюансов внутренней работы модуля форм. При всем моем уважении к этому фреймворку [...]

    Веб-обзор

    July 8, 2008 at 1:46 pm

  4. Just a note — while lazy loading will be helpful in many situations, caching your forms is still a good idea.While the validators and filters may not need to load for GET requests, there are still many, many objects floating around, and a lot of logic that has to be performed for rendering. So the takeaway is that the root issue — objects that were never used were being loaded — is corrected, the techniques you describe here are still sound.

  5. Hopefully i am not too profane, this line:

    $cache->save($form, $this->_formId);

    throws an Uncaught exception ‘Zend_Cache_Exception’ with message ‘Invalid id or tag : must be a string’

    iongion

    July 10, 2008 at 9:46 am

  6. That exception is thrown whenever you pass an invalid cache ID to the save() method. Check your $form variable and make sure you pass it as a string. More info here: Zend_Cache_Core, line 404.

    phpimpact

    July 10, 2008 at 9:56 am

  7. geez i’m experiencing the slowness right now..

    i have some fairly large select boxes with +300 elements per box (using zlib compression too)

    box 1 = countries
    box 2 = timezones
    box 3 = languages

    I’m not concerned with internationalization so i’ve explicitly disabled translation on each form element

    $oFormElement->setDisableTranslator(true);

    This helps alot. And in addition cached the form variable in an APC user variable. That helps even more… Its still not as zippy as i’d like but its alot better without it

    Looking forward to seeing Matthew’s changes! *phew*

    Quinton

    July 31, 2008 at 4:28 pm

  8. Yeah it really is amazing how slow Zend_Form can be. Normally my relatively complicated zend pages (with MVC) take about .5 seconds to load, with some modest zend_forms on them, we’re talking about 1.5 – 2 seconds per page load! (no caching, on my laptop).

    I noticed a lot of translation calls, so hopefully setting the setDisableTranslator flag will help on that end.

    To speed up Zend_Form loading i cache the actual Zend_Form object, not the rendered HTML, and i think im seeing some speed results with that. The problem with caching just the HTML is that even though the initial GET request of a form will be fast, you still need to rebuild the object on the postback to enable things like validation.

    John

    August 1, 2008 at 1:56 pm

  9. As John above mentioned, why don’t you cache the entire form object and use form’s class name as cache key ?

    iongion

    February 22, 2009 at 7:31 pm

  10. Caching objects in PHP doesn’t offer substantial benefits. The serialise function has its proper uses, but it’s a relatively slow process.

    Federico

    February 22, 2009 at 8:03 pm


Leave a Reply