Zend_Form Performance Issues
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
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.
Matthew Weier O'Phinney
July 7, 2008 at 3:51 pm
That was quick, thanks! (post updated)
phpimpact
July 7, 2008 at 5:42 pm
[...] Zend_Form Performance Issues – для любителей фреймворка от компании Zend интересное исследование производительности и вообще нюансов внутренней работы модуля форм. При всем моем уважении к этому фреймворку [...]
Веб-обзор
July 8, 2008 at 1:46 pm
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.
Matthew Weier O'Phinney
July 9, 2008 at 12:50 am
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
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
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
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
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
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