fede.carg / wiki

ZendForm Benchmarks

From Federico Cargnelutti

Jump to: navigation, search

Contents

Introduction

The Zend_Form component provides methods to dynamically create, validate and render HTML forms. 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.

The Problem

Performance issues

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
--> Create form object 
--> Load filters and validators 
--> Response

2. HTTP/1.1 POST

o-- Request 
--> Create form object 
--> 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 Solution

  • Add extra functionality to Zend_Form_Elements so that it checks for the requested method before loading dependencies, otherwise Zend_Form_Elements will use Reflection to allocate unnecessary objects into memory.
  • Cache the output of the Zend_Form object using Zend_Cache.

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

Testing Environment

Hardware

  • Intel Xeon @ 1.86GHz, 2GB RAM

Operating System

  • Red Hat 4.1.2

Software

  • Apache 2.2.8
  • ApacheBench 2.0.40
  • Zend Framework 1.5.2

Test configuration

Red Hat

127.0.0.1 localhost
127.0.0.1 www.zend-test.com zend-test.com

Apache Web Server

<VirtualHost *>
        ServerName www.zend-test.com
        ServerAlias zend-test.com
        DocumentRoot /home/zend-test/public
        <Directory /home/zend-test/public>
                Options Indexes FollowSymLinks MultiViews
                AllowOverride None
                Order allow,deny
                allow from all
        </Directory>
       RewriteEngine on
       RewriteRule !\.(js|ico|gif|jpg|png|css|html)$ /index.php
</VirtualHost>

ApacheBench

fede@dev:~$ ab -kc 10 -t 60 http://www.zend-test.com/

10 connections opened, using Keep-Alive, requesting http://www.zend-test.com/ for 60 seconds through those connections.

Zend Framework

application/
|-- bootstrap.php
|-- controllers
|   |-- ErrorController.php
|   |-- HtmlFormController.php
|   |-- ZendFormCachedController.php
|   `-- ZendFormController.php
|-- layouts
|   `-- zend-form.phtml
|-- models
`-- views
    `-- scripts
        |-- error
        |   |-- not-found.phtml
        |   |-- server-error.phtml
        |   `-- setup-error.phtml
        `-- index
            `-- index.phtml
library/
`-- Zend
public/
`-- index.php

Test result data

ZendFormController

Server Hostname:         www.zend-test.com
Document Path:           /zend-form
Requests per second 1:   36.51 [#/sec]
Requests per second 2:   36.72 [#/sec]
Requests per second 3:   36.54 [#/sec]

ZendFormCachedController

Server Hostname:         www.zend-test.com
Document Path:           /zend-form-cached
Requests per second 1:   81.54 [#/sec]
Requests per second 2:   81.38 [#/sec]
Requests per second 3:   82.01 [#/sec]

HtmlFormController

Server Hostname:         www.zend-test.com
Document Path:           /html-form
Requests per second 1:   94.55 [#/sec]
Requests per second 2:   94.63 [#/sec]
Requests per second 3:   94.49 [#/sec]

Classes

ZendFormController

class ZendFormController extends Zend_Controller_Action
{
	public function indexAction()
	{
		$this->getHelper('layout')->setLayout('zend-form');
		$this->view->form = $this->getForm(new Zend_Form);
	}
 
	public function getForm(Zend_Form $form) 
	{ 
		$form->setName('test_form');
		$form->setAction('/zend-form/edit');
		$form->setAttrib('action','edit');
		$form->setMethod('post');
 
		$form->clearDecorators();
		$form->addDecorator('FormElements')
			->addDecorator('HtmlTag', array('tag' => '<ul>'))
			->addDecorator('Form');
		$form->setElementDecorators(
			array(
				array('ViewHelper'),
				array('Errors'),
				array('Description'),
				array('Label', array('separator'=> '')),
				array('HtmlTag', array('tag' => 'li', 'class' => 'element-group')),
			)
		);
 
		$hiddenField = new Zend_Form_Element_Hidden('action');
		$form->addElement($hiddenField);
 
		$field1 = new Zend_Form_Element_Select('field1');
		$field1->setLabel('Field 1')
			->setMultiOptions(array('a'=>'a', 'b'=>'b'))
			->setRequired(true)
			->addValidator('NotEmpty', true);
		$form->addElement($field1);
 
		$field2 = new Zend_Form_Element_Text('field2');
		$field2->setLabel('Field 2')
			->setRequired(true)
			->addValidator('alnum')
			->addValidator('NotEmpty')
			->addValidator('regex', false, array('/^[a-z]/'))
			->addFilter('StringToLower');
		$form->addElement($field2);
 
		$field3 = new Zend_Form_Element_Textarea('field3');
		$field3->setLabel('Field 3')
			->setRequired(true)
			->addValidator('NotEmpty')
			->addFilter('StripTags'); 
		$form->addElement($field3);
 
		$field4 = new Zend_Form_Element_Text('field4');
		$field4->setLabel('Field 4')
			->setRequired(true)
			->addValidator('stringLength', false, array(20))
			->addValidator('EmailAddress')
			->addFilter('StringTrim'); 
		$form->addElement($field4);
 
		$field5 = new Zend_Form_Element_Text('field5');
		$field5->setLabel('Field 5')
			->setRequired(true)
			->addValidator('stringLength', false, array(20))
			->addValidator('NotEmpty');
		$form->addElement($field5);
 
		$field6 = new Zend_Form_Element_Radio('field6');
		$field6->setLabel('Field 6')
			->setRequired(true);
		$form->addElement($field6);
 
		$submit = new Zend_Form_Element_Submit('submit');
		$submit->setDecorators(
			array(
				array('ViewHelper'),
				array('Description'),
				array('HtmlTag', array('tag' => 'li', 'class' => 'submit-group')),
			)
		);
		$form->addElement($submit);
 
		$reset = new Zend_Form_Element_Reset('reset');
		$reset->setDecorators(
			array(
				array('ViewHelper'),
				array('Description'),
				array('HtmlTag', array('tag' => 'li', 'class' => 'submit-group')),
			)
		);
		$form->addElement($reset);
 
		return $form->render();
	}
}

ZendFormCachedController

class ZendFormCachedController extends Zend_Controller_Action
{
        protected $_formId = 'form';
 
        public function indexAction()
        {
                $frontendOptions = array(
                        'lifetime' => 7200,
                        'automatic_serialization' => true);
 
                $backendOptions = array('cache_dir' => '/tmp/');
                $cache = Zend_Cache::factory('Core', 'File', $frontendOptions, $backendOptions);
 
                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;
        }
 
	public function getForm(Zend_Form $form) 
	{
                ...
	}
}

HtmlFormCachedController

class HtmlFormController extends Zend_Controller_Action
{
	public function indexAction()
	{
		$this->getHelper('layout')->setLayout('html-form');
	}
}

Bootstrapper

define('APPLICATION_DIRECTORY', dirname(__FILE__));
set_include_path(dirname(APPLICATION_DIRECTORY) . '/library');
 
require_once 'Zend/Loader.php';
Zend_Loader::registerAutoload();
 
// set Model path in include path
set_include_path(get_include_path() . PATH_SEPARATOR . APPLICATION_DIRECTORY . '/models');
 
// bootstrap layouts
Zend_Layout::startMvc(array(
    'layoutPath' => APPLICATION_DIRECTORY . '/layouts',
    'layout' => 'main'
    ));
 
Zend_Controller_Front::run(APPLICATION_DIRECTORY . '/controllers');
Contact
Personal tools