Anymore structures stuff and you will have me hung!

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

Post Reply
User avatar
BDKR
DevNet Resident
Posts: 1207
Joined: Sat Jun 08, 2002 1:24 pm
Location: Florida
Contact:

Anymore structures stuff and you will have me hung!

Post by BDKR »

In spite of the title, I have to post this as I may have mislead some.

Anyways, a while back, there were these threads.

Can PHP realize something like structure in C
http://www.devnetwork.net/forums/viewtopic.php?p=8841

and

Structures in PHP revisited
http://www.devnetwork.net/forums/viewtopic.php?t=2677

Well, Matt Wade had created some benchmarking code to check the speed of the findings we came up with in the first thread. It pretty much confirmed the suspicion that mimicing the use of a structure in a class would be slower than doing so with an array.

Well, :cry:, that's only part of the story.

Once again, after finishing a project here in the office, I was ramping up for another but got back to thinking about this stuff and found something extremeley interesting. In short, the performance of associative arrays are terrible!

Take a look at the code below.

Code: Select all

<?
# Let's make sure we don't time out
set_time_limit(0);

function getmicrotime()
  { 
  list($usec, $sec) = explode(" ",microtime()); 
  return ((float)$usec + (float)$sec); 
  } 

class Struct 
  {
  var $count = 0;
  var $count2 = 0;
  var $count3 = 0;
  }

function Struct_arr_index()
  {
  $struct_array=array(
	0 => 0,
	2 => 0,
	3 => 0
	);
  return $struct_array;
  }

function Struct_arr_assoc()
  {
  $struct_array=array(
	count => 0,
	count2 => 0,
	count3 => 0
	);
  return $struct_array;
  }

# Test one. Using a class.
$struct = new Struct;
$time_start = getmicrotime();
for($i = 0;$i < 100000; $i++) 
  {
  $struct->count3 = $i;
   }
$time_end = getmicrotime();
echo "Using a class took: " . ($time_end - $time_start) . " seconds.<BR>\n\n";
# End test one. 


# Test two. Using an indexed array.
$array=Struct_arr_index();
$time_start = getmicrotime();
for($i = 0;$i < 100000; $i++) 
  {
  $arrayї3] = $i;
   }
$time_end = getmicrotime();
echo "Using an indexed array took: " . ($time_end - $time_start) . " seconds.<BR>\n\n";
# End test two.


# Test three. Using a plain array.
$array2 = Struct_arr_assoc();
$time_start = getmicrotime();
for($i = 0;$i < 100000; $i++) 
  {
  $array2їcount3] = $i;
   }
$time_end = getmicrotime();
echo "Using an associative array took: " . ($time_end - $time_start) . " seconds.<BR>\n\n";
# End test three.

?>
The first thing you'll notice is that the performance of the class is rather respectable in comparison to test2 using an indexed array. However, when you take a look at the results of test three, where an associative array is created, things start getting bad very quickly. Test three takes more than twice the amount of time to complete.

:?: :!:

Now for giggles, I ran the script again as such.

Code: Select all

<?
# Let's make sure we don't time out
set_time_limit(0);

function getmicrotime()
  { 
  list($usec, $sec) = explode(" ",microtime()); 
  return ((float)$usec + (float)$sec); 
  } 

class Struct 
  {
  var $count = 0;
  var $count2 = 0;
  var $count3 = 0;
  }

function Struct_arr_index()
  {
  $struct_array=array(
	0 => 0,
	2 => 0,
	3 => 0
	);
  return $struct_array;
  }

function Struct_arr_assoc()
  {
  $struct_array=array(
	count => 0,
	count2 => 0,
	count3 => 0
	);
  return $struct_array;
  }

# Test one. Using a class.
$struct = new Struct;
$time_start = getmicrotime();
for($i = 0;$i < 100000; $i++) 
  {
  $struct->count3 = $i;
  ++$struct->count;
  }
$time_end = getmicrotime();
echo "Using a class took: " . ($time_end - $time_start) . " seconds.<BR>\n\n";
# End test one. 


# Test two. Using an indexed array.
$array=Struct_arr_index();
$time_start = getmicrotime();
for($i = 0;$i < 100000; $i++) 
  {
  $arrayї3] = $i;
  ++$arrayї0];
  }
$time_end = getmicrotime();
echo "Using an indexed array took: " . ($time_end - $time_start) . " seconds.<BR>\n\n";
# End test two.


# Test three. Using a plain array.
$array2 = Struct_arr_assoc();
$time_start = getmicrotime();
for($i = 0;$i < 100000; $i++) 
  {
  $array2їcount3] = $i;
  ++$array2їcount];
  }
$time_end = getmicrotime();
echo "Using an associative array took: " . ($time_end - $time_start) . " seconds.<BR>\n\n";
# End test three.

?>
Now each test provides an additional operation on the class, or structure. I wanted to see if just moving around, in, or through any of these constructs created a performance hit. As it turns out, test three slows down even further and now takes over three times as long to complete.

That's crazy!

To summarize, if you are interested in mimicing the use of structures in PHP, the class approach is the best! If you are simply worried about speed at the expense of readability, then the Struct_array_index() function is what you want.

Now comparing the first two, I have to say that I would rather be able to use a name for an element than a number. The code is easier to read and maintain. I'll take the class approach.

If performance really is a concern, and it should be, then stay away from the third option as much as possible. It's slow as Christmast and the benchmarking bears this out.

Later on,
BDKR
User avatar
jonsyd
Forum Commoner
Posts: 36
Joined: Fri Sep 20, 2002 9:28 am
Location: Oxford, UK

Post by jonsyd »

Well spotted.

I've always avoided classes because I understood there would be a big performance hit, but have only just realised that I did my own tests using indexed-array structures vs classes.

Back in the real world of code production, I only ever really use associative-arrays (for legibility and comprehensibility). Given that classes shockingly seem to perform 100% better than associative-arrays, I'll take the 10% hit compared with indexed-arrays every time.

Besides, PHPUnit and XP are pulling me towards a completely OO approach anyway... :?

Jonny
User avatar
BDKR
DevNet Resident
Posts: 1207
Joined: Sat Jun 08, 2002 1:24 pm
Location: Florida
Contact:

Post by BDKR »

Hey J,
Back in the real world of code production, I only ever really use associative-arrays (for legibility and comprehensibility). Given that classes shockingly seem to perform 100% better than associative-arrays, I'll take the 10% hit compared with indexed-arrays every time.
This reminds me of somethjing. Since I've been doing this when I get spare rest periods at work, I've decided I'm not yet finished. I'm curious what effect adding methods to that class would have on it's performance. I'm going to write some in now. I think I'll test using an internal method to increment the referenced vars, and incrementing the vars from outside.

I hope that makes sense.

Caio,
BDKR
User avatar
BDKR
DevNet Resident
Posts: 1207
Joined: Sat Jun 08, 2002 1:24 pm
Location: Florida
Contact:

More info

Post by BDKR »

OK, I just added these bits of code to the test routine.

Code: Select all

<?php

class Struct_2
  {
  var $count = 0;
  var $count2 = 0;
  var $count3 = 0;
  
  function increment()
	{ ++$this->count3; }
 
  function increment2()
	{ ++$this->count; }
  }

# Test four. Using a class and it's internal methods to increment values.
$struct2 = new Struct_2;
$time_start = getmicrotime();
for($i = 0;$i < 1000000; $i++) 
  {
  $struct2->increment();
  $struct2->increment2();
  }
$time_end = getmicrotime();
echo "Using a class and it's methods took: " . ($time_end - $time_start) . " seconds.<BR>\n\n";
# End test four 

?>
Now the tests before this certainly seemed to paint the OOP stuff well, but these extra bits of code begin to expose where those oft spoken of performance hits occur with classes. Here are some number:

12.43 seconds for test one. Using a class with no methods
11.7 seconds for test two. Using an indexed array
32 seconds (!) for test three. Using an associative array
30 seconds for test four. Using a class and it's internal methods

What this shows is that an object performing operations on it's own internally referenced variables is going to be a bit slower. This we allready know. It's that performance hit that is often spoken of.

Check out this link along the same lines.
http://php.weblogs.com/discuss/msgReader$537?mode=day

Now the reason this is important is becuase we don't want to rest on, or come to the conclusion that everything is just fine with objects and that it's performance penalty is a myth. Objects ARE slower. There is nothing we can do about it. Comparing the results of the tests using classes, the test using it's own internal methods took nearly three times as long.

However, using a class with no methods as an approach to simulating the use of structures is a very good option from a performance view point. Not to mention that the code for accessing those structures in PHP looks just the same as it does in C.

Code: Select all

$struct->element
Oh well, back to work. :twisted:

Cheers,
BDKR
User avatar
jonsyd
Forum Commoner
Posts: 36
Joined: Fri Sep 20, 2002 9:28 am
Location: Oxford, UK

Post by jonsyd »

... yup, makes sense although I'm a little suspicious of the timings.

I added another class with a bunch of dummy methods:

Code: Select all

class Struct2  
  { 
  var $count = 0; 
  var $count2 = 0; 
  var $count3 = 0; 
  function doSomething1() {
  	}
  function doSomething2() {
  	}
  function doSomething3() {
  	}
  function doSomething4() {
  	}
  function doSomething5() {
  	}
  function doSomething6() {
  	}
  function doSomething7() {
  	}
  }
and the corresponding test:

Code: Select all

# Test one.one Using a class with a bunch of methods. 
$struct2 = new Struct2; 
$time_start = getmicrotime(); 
for($i = 0;$i < 100000; $i++)  
  { 
  $struct2->count3 = $i; 
  ++$struct2->count; 
  } 
$time_end = getmicrotime(); 
echo "Using a class with a bunch of methods took:<BR>
" . ($time_end - $time_start) . " seconds.<P>\n\n"; 
# End test one.one
The results show that the new class with the dummy methods performs a few% better than the one without. However, if I move the new test above the old one in the code, the older class performs better. Can't quite work out why, but perhaps we should be using Benchmark::Benchmark_Iterate from PEAR for consistency. Thoughts?

Jonny
User avatar
BDKR
DevNet Resident
Posts: 1207
Joined: Sat Jun 08, 2002 1:24 pm
Location: Florida
Contact:

Post by BDKR »

Hey J,

That's extremely interesting :!:

As for thoughts on the benchmarking and what you proposed, I"m not really familiar with Pear and made a decision in the past to keep it that way. But that's another topic. :roll:

Anyways, do you see a problem with the way the various hunks of code are benchmarked now? I don't, but that doesn't mean a problem isn't there.

Now after a cursory glance at the code you provided, it's not suprising that those dummy methods have little effect on the performance of the class as they aren't doing anything. I noticed that your test block is performing operations on the object elements directly as opposed to having a method do it. As I mentioned before, the slow down comes when performing operations on referenced (as in inside of an object) vars.

But all of this is really a fork from the original thrust of what we were doing. That being figuring out a way to mimic structures (like in C) in php.

One more thing, then I have to get back to work for the rest of the day as I have a critical hunk of code to look at before it runs again at 10:30 this evening. I really have an hard time with the idea of using a class for benchmarking. Particularly when some of what's being benchmarked isn't/aren't classes or objects. It seems like telling an pink elephant to balance on one leg in a marsh. In spite of it's behaviour, it's still not a flamingo.

Cheers,
BDKR
Post Reply