Recent Articles

Recent Comments

Resources

Accelerators Revisited

December 24, 2007

Updated 2007-12-30: I utilized the same configuration as previously with XCache in order to provide more detail to moo, the developer of XCache. After running these again, I found slightly different numbers in XCache (Still unknown why the previous numbers died in certain tests). The numbers have been updated below.

Due to feedback from the first time around for tuning settings I decided to re-run the tests. I found a much closer value this time, however, it seems that XCache had some odd results which I was unable to find out why they were so off. It might be that the variables were not configured to be cached? If anyone has any feedback on XCache settings let me know. The configuration that was utilized is shown at the end of the post. The details of the test are explained in a previous post outlining the details. There are some slight differences such as the configuration of some of the accelerators.


Here we had the bootstrap of the file using “require_once” on each of the included classes. Both APC and the Zend Platform are neck to neck here, however, the Zend Platform was ahead by 11 requests meanwhile XCache lagged behind at 986.


Here we had the bootstrap of the file using “require” on each of the included classes. Again APC and Zend Platform are comparable, however, APC was ahead by 14 requests meanwhile XCache lagged behind at 999.


Here we had the bootstrap of the file using “Zend_Loader::loadClass()” on each of the included classes. Again APC and Zend Platform are comparable, however, Zend Platform was ahead by 21 requests meanwhile XCache resulted in 881.

Configuration

The configuration of each item is explained below for reference.

APC


apc.enabled = 1
apc.shm_segments = 1
apc.shm_size = 30
apc.optimization = 0
apc.num_files_hint = 1000
apc.user_entries_hint = 4096
apc.ttl = 0
apc.user_ttl = 0
apc.gc_ttl = 3600
apc.cache_by_default = 1
apc.filters NULL
apc.mmap_file_mask NULL
apc.slam_defense = 0
apc.file_update_protection = 2
apc.enable_cli = 0
apc.max_file_size = 1M
apc.stat = 1
apc.write_lock = 1
apc.report_autofilter = 0
apc.include_once_override = 0
apc.localcache = 0
apc.localcache.size = 512

XCache


xcache.shm_scheme = "mmap"
xcache.size = 30M
xcache.count = 1
xcache.slots = 8K
xcache.ttl = 0
xcache.gc_interval = 0
xcache.var_size = 0M
xcache.var_count = 1
xcache.var_slots = 8K
xcache.var_ttl = 0
xcache.var_maxttl = 0
xcache.var_gc_interval = 300
xcache.test = Off
xcache.readonly_protection = Off
xcache.mmap_path = "/dev/zero"
xcache.coredump_directory = ""
xcache.cacher = On
xcache.stat = On
xcache.optimizer = On

Zend Platform

“If you want to level the field, than you should set the acceleration to
extreme via the GUI and load only ZendPlatform.so (every extension from
Monitor to Debugger and Optimizer should be removed in php.ini)

You should then enable timestamp checking (change
zend_accelerator.validate_timestamps=0 to 1) because APC does that by
default (it’s slower but this will give level playing field and many
people prefer it on as they don’t need to restart Apache every time a
file is updated).”

Conclusions

APC and Zend Platform seem to be the strongest and APC which is available in PECL otherwise the Zend Platform is for purchase. What I thought a little off was that in order to get the Zend Platform to perform nicely, you had to turn off many of the different features which makes me think that it would either work better in a large server farm for a single platform or in a testing environment where a QA staff is checking everything.

Now this test isn’t exactly perfect such that the configuration isn’t perfectly tuned for each item. I know for myself I have decided on using APC after running all of these, however, XCache does seem like something I want to use in the future.

15 Comments

PHP Accelerators: The Missing Details

December 23, 2007

Since the overwhelming response to provide more details about my test, I have here. You can see the files (some items stripped for not telling everyone on the earth what I am building — that will be another post when it’s finished). I will also retest with some of the noted information that have been in the comments. After I did this test, I took out the Zend_Loader and did require_once which then APC flew in the first post. I am looking to change these all to require but didn’t want to take the time early this morning to figure out which classes I needed to remove to have simply require. Essentially I was pretty tired from my all night code session. Once I change all of these details I will retest. All of the caching and optimization tools were only the default configurations.

Test Server Details

  • P4 2.4Ghz
  • 1GB DDR 266
  • 80GB 7200RPM IDE
  • RHEL ES 4.0
  • PHP 5.2.5
  • MySQL 5.0.45
  • Apache 2.2.6

Testing Methodology

Between every test PHP extensions were added or removed (XCache w/ and w/o Zend . After these extensions were added or removed; I restarted apache. The load was always at approximately .05 when the test was started on each individual. The test was only done with ab: ab -c 10 -t 60 http://***********/index/login (this way I would miss the redirect).

The Files

Please note the test of PHP Accelerators utilized the commented lines (the lines with Zend_Loader).

Index

require('../lib/Pmt/Base.php');
Pmt_Base::run();

Bootstrap

define('PMT_PATH', realpath(dirname(__FILE__) . '/../../'));
define('PMT_PATH_APP',  PMT_PATH . '/app');
define('PMT_PATH_WWW',  PMT_PATH . '/www');
define('PMT_PATH_LIB',  PMT_PATH . '/lib');
define('PMT_PATH_CONF', PMT_PATH . '/conf');
define('PMT_PATH_TMP',  PMT_PATH . '/tmp');
 
set_include_path(get_include_path() . PATH_SEPARATOR . PMT_PATH_LIB);
 
require_once(PMT_PATH_LIB . '/Zend/Loader.php');
Zend_Loader::loadClass('Zend_Cache');
Zend_Loader::loadClass('Zend_Config_Xml');
Zend_Loader::loadClass('Zend_Db');
Zend_Loader::loadClass('Zend_Db_Table');
Zend_Loader::loadClass('Zend_Registry');
 
final class Pmt_Base {
	/**
	 * Initialize Base
	 * This initializes all of the components and settings
	 * that must be set in order for items to remain consistant.
	 */
	public static function init() {
		$conf = new Zend_Config_Xml(PMT_PATH_CONF . '/base.xml', 'development');
		error_reporting($conf->server->php->error);
		date_default_timezone_set($conf->server->timezone);
		$db = Zend_Db::factory($conf->db->type, array(
			'host'     => $conf->db->host,
			'username' => $conf->db->user,
			'password' => $conf->db->pass,
			'dbname'   => $conf->db->name,
			'options'  => array(Zend_Db::CASE_FOLDING => Zend_Db::CASE_LOWER)
		));
		Zend_Db_Table_Abstract::setDefaultAdapter($db);
 
		$cache = Zend_Cache::factory(
			'Core',
			'File',
			array('automatic_serialization' => true),
			array('cacheDir' => PMT_PATH_TMP . '/cache')
		);
		Zend_Db_Table_Abstract::setDefaultMetadataCache($cache);
 
		Zend_Registry::set('conf', $conf);
		Zend_Registry::set('db', $db);
		Zend_Registry::set('cache', $cache);
	}
 
	/**
	 * Run Base
	 * This will run the main website and initilize different
	 * items that are strictly for the website usage as well
	 * as handling all of the aspects of the controllers.
	 */
	public static function run() {
		Pmt_Base::init();
		Zend_Loader::loadClass('Zend_Controller_Front');
		Zend_Loader::loadClass('Pmt_Controller_Action');
		Zend_Loader::loadClass('Zend_Auth');
		Zend_Loader::loadClass('Zend_Acl');
		Zend_Loader::loadClass('Zend_Acl_Resource');
		Zend_Loader::loadClass('Zend_Acl_Role');
		Zend_Loader::loadClass('Pmt_Acl');
		Zend_Loader::loadClass('Zend_Session');
		Zend_Loader::loadClass('Zend_Session_Namespace');
 
		Zend_Session::setOptions(array('save_path' => PMT_PATH_TMP . '/session'));
 
		$acl  = new Pmt_Acl();
		$auth = Zend_Auth::getInstance();
		$session = new Zend_Session_Namespace('Default');
 
		Zend_Registry::set('acl', $acl);
		Zend_Registry::set('auth', $auth);
		Zend_Registry::set('session', $session);
 
		$cntl = Zend_Controller_Front::getInstance();
		$cntl->throwExceptions(true)
		     ->setControllerDirectory(array(
				'default'  => PMT_PATH_APP . '/default/cntl'
			 ))
			 ->dispatch();
	}
}

Pmt_Acl

class Pmt_Acl extends Zend_Acl {
	public function __construct() {
		$this->add(new Zend_Acl_Resource('index'));
		$this->add(new Zend_Acl_Resource('dashboard'));
		$this->add(new Zend_Acl_Resource('*****'));
		$this->add(new Zend_Acl_Resource('*****'));
		$this->add(new Zend_Acl_Resource('*****'));
		$this->add(new Zend_Acl_Resource('*****'));
		$this->add(new Zend_Acl_Resource('*****'));
 
        $this->addRole(new Zend_Acl_Role('guest')); 
        $this->addRole(new Zend_Acl_Role('member'), 'guest');
 
        $this->deny('guest');
        $this->allow('guest', 'index');
        $this->allow('member');
	}
}

Pmt_Controller_Action

class Pmt_Controller_Action extends Zend_Controller_Action {
	function preDispatch() {
		$auth = Zend_Registry::get('auth');
		$acl = Zend_Registry::get('acl');
		$role = (!$auth->hasIdentity()) ? 'guest' : 'member';
		$resource = $this->_request->getControllerName();
		$action = $this->_request->getActionName();
		if (!$acl->has($resource)) {
			$resource = null;
		}
 
		if (!$acl->isAllowed($role, $resource, $action)) {
			if (!$auth->hasIdentity()) {
				$this->_redirect('/index/login');
			} else {
				$this->_redirect('/error/privileges');
			}
		}
	}
}

The Login Page

The form class is a custom render for HTML_QuickForms — you can see this file next

	public function loginAction()	{
		Zend_Loader::loadClass('Pmt/Form.php');
		$f = new Pmt_Form('login', 'post');
		$f->addElement('text', 'username', 'Username:', array('maxlength'=>25))
		  ->addRule('userName', 'Please enter a username', 'required')
		  ->addElement('password', 'password', 'Password:', array('maxlength'=>32))
		  ->addRule('password', 'Please enter a password', 'required')
		  ->addElement('submit', 'submit', 'Login');
 
		if ($f->validate() && ($clean = $f->exportValues())) {
			//this is never run inside here.
		}
		$this->view->login_form = $f->render();
	}

Pmt_Form

You may be wondering why I dynamically turn on and off error reporting here. Well HTML_QuickForms isn’t exactly PHP 5 nice and has quite a bit of warnings and/or strict errors. It actually speeds things up since the error reporting is not having to work).

$err = error_reporting(0);
require_once('HTML/QuickForm.php');
require_once('HTML/QuickForm/Renderer/Tableless.php');
error_reporting($err);
unset($err);
 
class Pmt_Form extends HTML_QuickForm
{
	private $_err;
 
	public function __construct($name='form', $method='post', $url='')
	{
		$this->_err = error_reporting(0);
 
		$url = (empty($url)) ? $_SERVER['REQUEST_URI'] : $url;
		parent::HTML_QuickForm($name, $method, $url);
		$this->removeAttribute('name');
		return $this;
	}
 
	public function addRule($element, $message, $type, $format=null, $validation='server', $reset=false, $force=false)
	{
		parent::addRule($element, $message, $type, $format=null, $validation='server', $reset=false, $force=false);
		return $this;
	}
 
	public function &addElement($element)
	{
		$a = func_get_args();
		$as = array();
		for($c=0;$c<count ($a);$c++)
		{
			$as[] = '$a[' . $c . ']'; 
		}
		$as = implode(', ', $as);
		eval('parent::addElement(' . $as . ');');	
		return $this;
	}
 
	public function exportValues($e = null)
	{
		$ret = parent::exportValues($e);
		error_reporting($this->_err);
		return $ret;
	}
 
	public function render()
	{
		$r = new HTML_QuickForm_Renderer_Tableless();
		$this->accept($r);
		$ret = $r->toHtml();
		error_reporting($this->_err);
		return $ret;
	}
}
</count>

13 Comments

Zend Framework Performance Zend_Loader

December 23, 2007

This post is related to the last post on PHP Accelerators and utilizing the Zend Framework. I changed out the Zend_Loader::loadClass() implementation that I was utilizing to load all of the classes and found that it drastically slowed things down. I am not just talking slightly, but a massive change in performance.

In the previous post APC was the fastest with 900 requests. After taking the Zend_Loader out of the picture in the bootstraps and moved them to a require_once function (I attempted just simply require, but that caused some issues likely due to some conflicts with the Zend Framework requiring their own files already in specific instances.

Results

The APC requests went from 914 to 27,411 with the same ab bench.

Concurrency Level:      10
Time taken for tests:   60.3722 seconds
Complete requests:      27411
Failed requests:        0
Write errors:           0
Total transferred:      5976688 bytes
HTML transferred:       0 bytes
Requests per second:    456.82 [#/sec] (mean)
Time per request:       21.890 [ms] (mean)
Time per request:       2.189 [ms] (mean, across all concurrent requests)
Transfer rate:          97.26 [Kbytes/sec] received

If you don’t know it by now, just for clarity, DO NOT USE Zend_Loader in your applications unless you would like to see it drastically slow down.

11 Comments

PHP Accelerators : APC vs Zend vs XCache with Zend Framework

December 23, 2007

Tonight I wanted to see how the Zend Framework would run against an application that I have put in quite a bit of components into. I figured with the amount of objects that have been instanciated that there was likely going to be a larger performance hit overall. These results may surprise you with what really made a difference as far as PHP Accelerators.

Zend Framework : Release 1.0.3

  • Zend_Cache
  • Zend_Config
  • Zend_Config_Xml
  • Zend_Db
  • Zend_Db_Table
  • Zend_Registry
  • Zend_Loader
  • Zend_Controller_Front
  • Zend_Auth
  • Zend_Acl
  • Zend_Acl_Role
  • Zend_Acl_Resource
  • Zend_Session
  • Zend_Session_Namespace

Testing Environment

  • PHP 5.2.5
  • Apache 2.2.6

Test Results

Pure PHP

Concurrency Level:      10
Time taken for tests:   60.655071 seconds
Complete requests:      298
Failed requests:        0
Write errors:           0
Total transferred:      643149 bytes
HTML transferred:       516971 bytes
Requests per second:    4.91 [#/sec] (mean)
Time per request:       2035.405 [ms] (mean)
Time per request:       203.541 [ms] (mean, across all concurrent requests)
Transfer rate:          10.35 [Kbytes/sec] received

APC

Concurrency Level:      10
Time taken for tests:   60.106697 seconds
Complete requests:      914
Failed requests:        0
Write errors:           0
Total transferred:      1962675 bytes
HTML transferred:       1582035 bytes
Requests per second:    15.21 [#/sec] (mean)
Time per request:       657.623 [ms] (mean)
Time per request:       65.762 [ms] (mean, across all concurrent requests)
Transfer rate:          31.88 [Kbytes/sec] received

Zend Optimizer

Concurrency Level:      10
Time taken for tests:   60.302711 seconds
Complete requests:      262
Failed requests:        0
Write errors:           0
Total transferred:      564135 bytes
HTML transferred:       454727 bytes
Requests per second:    4.34 [#/sec] (mean)
Time per request:       2301.630 [ms] (mean)
Time per request:       230.163 [ms] (mean, across all concurrent requests)
Transfer rate:          9.12 [Kbytes/sec] received

XCache

Concurrency Level:      10
Time taken for tests:   60.261114 seconds
Complete requests:      292
Failed requests:        0
Write errors:           0
Total transferred:      628485 bytes
HTML transferred:       506597 bytes
Requests per second:    4.85 [#/sec] (mean)
Time per request:       2063.737 [ms] (mean)
Time per request:       206.374 [ms] (mean, across all concurrent requests)
Transfer rate:          10.17 [Kbytes/sec] received

Conclusions

What I find quite interesting about this, is that APC was the only one to provide a much higher amount of requests while all the others slowed the requests down. I believe this may be related to utilizing the Zend_Loader functionality which also slows some items down for anything attempting to optimize and cache. I believe after I change that I am going to run these tests again and see if there is any large speed changes. I will post more about this more than likely in a week or so.

14 Comments