Using Timers to Benchmark PHP Applications

If you’ve been using PHP for a while, then it’s possible that you want to learn how to create benchmarking scripts. If this is true, then this series of tutorials will be pretty helpful. Welcome to the second article of the series “Benchmarking applications with PHP.” Composed of three parts, this series walks you through the development of several approaches aimed at benchmarking specific blocks of code and even entire PHP applications.

As you’ll surely recall from the last tutorial, I discussed distinct approaches for benchmarking concrete portions of code and even complete programs. These methods, which were originally created either as procedural solutions or object-based techniques, used the “microtime()” PHP built-in function to create simple timing functions along with a pair of useful timer classes.

Even though all the methods that I mentioned before are really simple to understand and follow rather basic logic, they’re also useful for creating more sophisticated timing solutions that eventually might fit the requirements of high-performance applications. I’m only going to provide you with some handy pointers on developing these kinds of timing mechanisms; what you do with them is obviously up to you.

Well, having refreshed the topics discussed in the first tutorial of the series, it’s time to go over the subject of this one. This way you will know what to expect from it before you start reading.

Since you already know how to create timer functions and classes, in this tutorial, I’ll create some concrete examples that show where these timing mechanisms can be applied in order to evaluate the performance of certain PHP applications.

Are you ready to learn more about how to benchmark applications with PHP? Let’s get started!

{mospagebreak title=Defining a few working classes}

The first example that I plan to develop here consists of demonstrating how the timer class that I defined in the first article can be used for benchmarking the performance of a PHP application that fetches the contents of a few simple database rows and prints them on the screen.

In this first case, I’m going to perform all this without using HTTP compression when the data is sent to the client. In the second example, all of the data will be compressed when transmitted across the network.

Obviously, the timer class will be tasked with timing each of the applications that I mentioned before, indicating which one has the better performance. Also, it should be noted that all the tests will be done using a local host. Thus, the eventual differences that appear between the two examples could be even more significant, if they’re deployed on remote servers.

Having clarified this point, here is the complete list of classes that will be used with the first example. Have a look at them, please:


// define ‘MySQL’ class


class MySQL{

private $conId;

private $host;

private $user;

private $password;

private $database;

private $result;

const OPTIONS=4;

public function __construct($options=array()){

if(count($options)!=self::OPTIONS){

throw new Exception(‘Invalid number of connection parameters’);

}

foreach($options as $parameter=>$value){

if(!$value){

throw new Exception(‘Invalid parameter ‘.$parameter);

}

$this->{$parameter}=$value;

}

$this->connectDB();

}

// connect to MySQL

private function connectDB(){

if(!$this->conId=mysql_connect($this->host,$this->user,$this->password)){

throw new Exception(‘Error connecting to the server’);

}

if(!mysql_select_db($this->database,$this->conId)){

throw new Exception(‘Error selecting database’);

}

}

// run query

public function query($query){

if(!$this->result=mysql_query($query,$this->conId)){

throw new Exception(‘Error performing query ‘.$query);

}

return new Result($this,$this->result);

}

}


// define ‘Result’ class


class Result {

private $mysql;

private $result;

public function __construct($mysql,$result){

$this->mysql=$mysql;

$this->result=$result;

}

// fetch row

public function fetchRow(){

return mysql_fetch_assoc($this->result);

}

// count rows

public function countRows(){

if(!$rows=mysql_num_rows($this->result)){

throw new Exception(‘Error counting rows’);

}

return $rows;

}

// count affected rows

public function countAffectedRows(){

if(!$rows=mysql_affected_rows($this->mysql->conId)){

throw new Exception(‘Error counting affected rows’);

}

return $rows;

}

// get ID from last-inserted row

public function getInsertID(){

if(!$id=mysql_insert_id($this->mysql->conId)){

throw new Exception(‘Error getting ID’);

}

return $id;

}

// seek row

public function seekRow($row=0){

if(!is_int($row)||$row<0){

throw new Exception(‘Invalid result set offset’);

}

if(!mysql_data_seek($this->result,$row)){

throw new Exception(‘Error seeking data’);

}

}

}


// define ‘Timer’ class


class Timer{

private $elapsedTime;

// start timer

public function start(){

if(!$this->elapsedTime=$this->getMicrotime()){

throw new Exception(‘Error obtaining start time!’);

}

}

// stop timer

public function stop(){

if(!$this->elapsedTime=round($this->getMicrotime()-$this->elapsedTime,5)){

throw new Exception(‘Error obtaining stop time!’);

}

return $this->elapsedTime;

}

//define private ‘getMicrotime()’ method

private function getMicrotime(){

list($useg,$seg)=explode(‘ ‘,microtime());

return ((float)$useg+(float)$seg);

}

}

Well, that’s all for now. As you can see, I defined three simple classes. The first two will be used to connect to MySQL and fetch different result sets. The third one (that is the “Timer” class) will be utilized to roughly time the process of retrieving some rows from a sample database table. Pretty easy, right?

Now that you know how all the previous classes will be put to work in a single example, go ahead and read the following section. That’s where I’ll show you how to benchmark the retrieval of database rows.

{mospagebreak title=Displaying database rows without using HTTP compression}

As I stated in the section that you just read, the first benchmarking example that I’ll set up will be aimed at determining how long it takes to fetch ten rows from a sample “USERS” MySQL database table. They will be displayed on the browser after the retrieval process. In this case, all these tasks will be performed without using HTTP compression when the data is transferred back to the client.

Assuming that the sample database table has been populated with the following data,


ID

Name

Email

1

user1

user1@domain.com

2

user2

user2@domain.com

3

user3

user3@domain.com

4

user4

user4@domain.com

5

user5

user5@domain.com

6

user6

user6@domain.com

7

user7

user7@domain.com

8

user8

user8@domain.com

9

user9

user9@domain.com

10

user10

user10@domain.com


here is the corresponding code sample along with the respective timing results:


// example without using HTTP compression


try{

// instantiate ‘Timer’ class

$timer=new Timer();

// start timer

$timer->start();

// connect to MySQL

$db=new MySQL(array
(‘host’=>’host’,’user’=>’user’,’password’=>’password’,’database’=>’database’));

$result=$db->query(‘SELECT * FROM users’);

while($row=$result->fetchRow()){

echo ‘ID: ‘.$row['id'].’ Name: ‘.$row['name'].’ Email: ‘.$row['email'].'<br />';

}

$elapsedTime=$timer->stop();

// display elapsed time

echo ‘Time spent in fetching database rows was ‘.$elapsedTime.’ seconds';

}

catch(Exception $e){

echo $e->getMessage();

exit();

}


/* displays the following:


ID: 1 Name: user1 Email: user1@domain.com

ID: 2 Name: user2 Email: user2@domain.com

ID: 3 Name: user3 Email: user3@domain.com

ID: 4 Name: user4 Email: user4@domain.com

ID: 5 Name: user5 Email: user5@domain.com

ID: 6 Name: user6 Email: user6@domain.com

ID: 7 Name: user7 Email: user7@domain.com

ID: 8 Name: user8 Email: user8@domain.com

ID: 9 Name: user9 Email: user9@domain.com

ID: 10 Name: user10 Email: user10@domain.com


Time spent in fetching database rows was 0.0053 seconds


/*


As you can see, the benchmarking script shown above is very simple to grasp. Basically, all it does is time the retrieval of the rows from MySQL, including the connection to the server. Obviously, this process is performed very quickly, since only a trivial query is executed against the sample database table. But in this case, I opted to display the data without using HTTP compression.

At this point, I’m pretty sure that you understand how to correctly use the “Timer” class that I defined a few lines above. Therefore, it’s time to leap forward and set up a similar example, but this time the database rows will be fetched and sent to the client using HTTP compression.

Go ahead and read the following section.

{mospagebreak title=Displaying database records using HTTP compression}

According to the concepts that I expressed in the previous section, the second benchmarking example that I plan to develop will be similar to the first one. However, the main difference will involve displaying the respective database rows using HTTP compression. This means that if everything goes as expected, there should be a small reduction in the time it takes to display these rows on the browser.

Having explained how the benchmarking test in question will be performed, what I’ll do next is define an additional class, called “DataCompressor,” that will be tasked with compressing any data inputted into its constructor via the GZIP algorithm. That said, here is the signature for this brand new class:


// define ‘DataCompressor’ class


class DataCompressor{

private $data;

public function __construct($data){

if(!is_string($data)){

throw new Exception(‘Data must be a string!’);

}

$this->data=$data;

}

public function compressData(){

// remove white spaces from (X)HTML code

$this->data=preg_replace(array("/r/","/n/"),”,$this->data);

// check if browser supports gzip encoding

if(strstr($_SERVER['HTTP_ACCEPT_ENCODING'],’gzip’)){

// start output buffer

ob_start();

// echo page contents to output buffer

echo $this->data;

// compress data with gzip

$this->data=gzencode(ob_get_clean(),9);

// send content encoding http header

header(‘Content-Encoding: gzip’);

}

return $this->data;

}

}


As shown above, the “DataCompressor” class compresses any string passed as an argument to the respective constructor via its “compressData()” method and returns this data to calling code.

As you’ll realize, the logic implemented by this class is pretty easy to grasp. Therefore, pay attention to the following example, which fetches the same ten rows from the previous “USERS” MySQL database table and displays them on the browser, but this time using HTTP compression:


// example using HTTP compression

try{

// instantiate ‘Timer’ class

$timer=new Timer();

// start timer

$timer->start();

// connect to MySQL

$db=new MySQL(array
(‘host’=>’host’,’user’=>’user’,’password’=>’password’,’database’=>’database’));

$result=$db->query(‘SELECT * FROM users’);

$data=”;

while($row=$result->fetchRow()){

$data.=’ID: ‘.$row['id'].’ Name: ‘.$row['name'].’ Email: ‘.$row['email'].'<br />';

}

$data.=’Time spent in fetching database rows was ‘.$timer->stop().’ seconds';

// use ‘DataCompressor’ object

$dataComp=new DataCompressor($data);

echo $dataComp->compressData();

}

catch(Exception $e){

echo $e->getMessage();

exit();

}


/* displays the following


ID: 1 Name: user1 Email: user1@domain.com

ID: 2 Name: user2 Email: user2@domain.com

ID: 3 Name: user3 Email: user3@domain.com

ID: 4 Name: user4 Email: user4@domain.com

ID: 5 Name: user5 Email: user5@domain.com

ID: 6 Name: user6 Email: user6@domain.com

ID: 7 Name: user7 Email: user7@domain.com

ID: 8 Name: user8 Email: user8@domain.com

ID: 9 Name: user9 Email: user9@domain.com

ID: 10 Name: user10 Email: user10@domain.com

Time spent in fetching database rows was 0.0049 seconds


*/


As you can see, the previous example shows a small, almost negligible difference when compared to the first case, because database rows were sent to the client compressed. However, you must remember that the two examples were tested with a local server. And even considering this condition, the overall result was slightly faster when HTTP compression was applied to the respective data.

On one hand, you learned how to define a timer class with PHP 5, while on the other, you saw two concrete cases where this class was used with pretty good results. Of course, my main recommendation here is that you test the previous timer class with your own PHP applications to evaluate their real performance.

Final thoughts

Finally, we’ve come to the end of this article. In this second tutorial of the series, I developed a couple of hands-on examples with the purpose of using a timer class to benchmark their respective performances. I hope this experience was pretty instructive for you.

In the last part of the series, I’ll show you how to use the same timer class, but this time to evaluate the difference between fetching rows from a database table and retrieving content from a text file.

See you in the last article!

[gp-comments width="770" linklove="off" ]

antalya escort bayan antalya escort bayan Antalya escort diyarbakir escort