Scaling to thousands of users is often a problem for web applications – and in fact any system that has a sudden large influx of new users. In recent times we have had a situation where we had inherited a fairly simple web application that was massively under performing when a single user accessed a reasonably large dataset from a MySQL database.

Immediately we blamed the database – it must be the bottleneck – located on a different server and transferring a result in excess of 10,000 records. But when performing the query from a DB client there was virtually nothing in terms of a delay.

So what was the problem? The code was reasonably simple. An in house database abstraction layer was used to construct a representation of each row returned from the database and stored it in a big array. Common OO style promoted the use of a series of ‘setter’ methods to store the data in each constructed object.

class MyClass
{
        private $animal = null;
        private $mineral = null;
        private $vegetable = null;
 
        public function __construct()
        {
        }
 
        public function setAnimal($animal)
        {
                $this->animal = $animal;
        }
 
        public function setMineral($mineral)
        {
                $this->mineral = $mineral;
        }
 
        public function setVegetable($vegetable)
        {
                $this->vegetable = $vegetable;
        }
}

This block of code simulated how each of the objects were being built – using the data as returned from the database.

for($i=0; $i<10000; $i++) {
        $test = new MyClass();
        $test->setAnimal('lion');
        $test->setMineral('stone');
        $test->setVegetable('potato');
}

Surprisingly (or perhaps not…) this code takes around 4.5 seconds to run (obviously depends on hardware!). Quite an overhead when you’re targeting a 2 seconds per page load time!

Looking at the code – the constructor is clearly redundant. It’s not serving any useful purpose. Removing it cut off around half a second of page load time (average).

Removing one of the setters saved another second or so… removing all the setters (just constructing a blank object 10,000 times cut’s it down to 1/10th of a second! Could method calls in php be that expensive?

We can create the same result by doing all of this in a constructor – consider the following.

class MyClass
{
        private $animal = null;
        private $mineral = null;
        private $vegetable = null;
 
        public function __construct($animal, $mineral, $vegetable)
        {
                $this->animal = $animal;
                $this->mineral = $mineral;
                $this->vegetable = $vegetable;
        }
}
 
for($i=0; $i<10000; $i++) {
        $test = new MyClass('lion', 'stone', 'potato');
}

This cut’s things down to just under two seconds – definitely better – but not where we want to be. We know that the presence of the constructor (presumably it’s just the overhead of the method call itself) is costing us some time. What if the class was simply a data container and offered nothing in the way of accessors?

class MyClass
{
        public $animal = null;
        public $mineral = null;
        public $vegetable = null;
}
 
for($i=0; $i<10000; $i++) {
        $test = new MyClass();
        $test->animal = 'lion';
        $test->mineral = 'stone';
        $test->vegetable = 'potato';
}

3/10ths of a second. Ok – now we are getting somewhere! For 10k results – this is probably acceptable.

It appears that object creation and method calling is actually quite an expensive operation within PHP – if we actually had to deal with 100k rows, the fastest way of doing it is always to just use an array. The 10k test completes in less than 1/10th of a second.

A more accurate analysis of just how much object instantiation and method calls cost could be done using xdebug to profile how long each line of code actually takes to execute. But for my purposes, this worked out well.