?

Log in

No account? Create an account
entries friends calendar profile It's Me Previous Previous Next Next
The Autobiography of Russell
Life from a different perspective
zimzat
zimzat
Some PHP profiling results
I was just doing some profiling code in PHP so I figured I would share the results.

if-else is better than $var = (condition) ? true : false; 2/3 of the time. Tested 30 times at 2,000,000 times each. Difference was negligable in all cases.

using list() = each() is better than foreach() if you process the same array more than once, otherwise foreach() is better at least 3/4 of the time.
Turns out this was an error on my part. foreach() automatically resets the array before it starts, while list() = each() doesn't, thus in my code it said "We're at the end. We're done already" and stopped. After adding a manual reset() the foreach() code was always faster.

foreach() is better than for()'ing through array_keys() 29 times out of 30. Last time was probably due to hiccup in system.

You can download the profiler code here. It's set up to allow you to add a couple of functions and then compare their execution time X number of times (10 being default X). Very simple.
(updated file on server to reflect changes in code)

Current Mood: tired tired
Current Music: "Universe" by Savage Garden

11 comments or Leave a comment
Comments
swanhart From: swanhart Date: July 27th, 2005 09:08 pm (UTC) (Link)
if-else is better than $var = (condition) ? true : false; 2/3 of the time. Tested 30 times at 2,000,000 times each. Difference was negligable in all cases.


That makes sense, because the compiler has to create an explicit parse tree for the if/else when the ternary operator is used. Basically, in PHP the ternary operator is a define that creates an if/else construct. That isn't the case in C, where the conversion is done once at compile time, but the opcodes between the if/else and ternary are the same at run time.

The reason it is only faster 2/3 of the time is probably due to latency in your system and the timer. I'd be surprised if ?: is ever faster than explicit if/else in PHP.

Still, for the minor performance difference, using the ternary operator makes sense for clean code.


using list() = each() is better than foreach() if you process the same array more than once, otherwise foreach() is better at least 3/4 of the time.

I think foreach does an explicit reset() of the array.

foreach() is better than for()'ing through array_keys() 29 times out of 30. Last time was probably due to hiccup in system.
That is because a lot more memory copying and dereferencing needs to be done in the second approach..
zimzat From: zimzat Date: July 27th, 2005 09:32 pm (UTC) (Link)
Using ?: makes for cleaner code? *balks* En-gah! This is some actual code found in phpBB and code similar to it is found all over the place. I do not call this clean, much less easy to debug.

$html_on = ( $submit || $refresh ) ? ( ( !empty($HTTP_POST_VARS['disable_html']) ) ? 0 : TRUE ) : ( ( $userdata['user_id'] == ANONYMOUS ) ? $board_config['allow_html'] : $userdata['user_allowhtml'] );



Looks like that did it. I put in a reset($array); before the list() = each() and it changed it to foreach() being faster all the time (not just the first time).


The reason I was testing these was because I was talking to someone who is trying to make the fastest code possible. They were being told that list() = each() is faster than using a foreach(), that ?: was faster than if-else, and using array_keys() to get just the keys is faster than foreach()'ing through all the values and keys. I was making individual test codes to check if what they were saying is true and after the second one it evolved into generic test functions.
swanhart From: swanhart Date: July 27th, 2005 10:26 pm (UTC) (Link)
Well, any language construct can be used to make bad code..
?: can be used to make good code or bad code, depending on the coder. Nesting ternary code is usually a bad idea.

Also, PHP performs differently on different platforms and PHP4 is much different from PHP5.
zimzat From: zimzat Date: July 27th, 2005 11:13 pm (UTC) (Link)
I was just talking to my brother about the ternary operator and he said that about them as well.

I'm considering lightening my restriction on the ternary operator just enough to allow a very simple $var = (small_condition) ? true : false; similar to the limit that I've lightened not using braces with very simple if statements. Not that adding an extra line to if ($this) $var .= 'blah'; else $var .= 'meh'; (The latter seems to have more control over what the condition operates on than a ternary operator...)

phpBB used to be something of an idol to me because that's where I started and learned most of my basic knowledge, but I'm starting to lose that image of it because of the nexted ternary operators and several other things. Maybe it'll get better with phpBB 3.
swanhart From: swanhart Date: July 28th, 2005 06:24 pm (UTC) (Link)
You can use any statement with the ternary operator. Because of OOP it is usually a good idea to enclose the statement in () if it is anything more than a simple evaluation.

Here are some of the ways I use it.. You can tell me if you think they are good or bad..

/* statement handle may not have been passed to the function,
if it was then check to see if there is an error condition
on the handle. Always check error condition on database handle */

if ( (empty($stmt) ? false : ocierror($stmt)) ||
ocierror($this->_DB_Link) )


/* If we want to commit the transaction, pass the proper OCI constant*/
$returnval = @OCIExecute($insert, $commit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT);
zimzat From: zimzat Date: July 28th, 2005 08:11 pm (UTC) (Link)

In the first one you're embedding a ternary operator in an if statement while at the same time running an operation and capturing its output. Isn't that kind of like nesting ternary operators in a way? It does seem a little confusing and I question the order of operation, and if the ecierror($this->_DB_Link) even gets run in the statement.

The second one makes sense but it hinges on seeing the question mark to know that it isn't actually passing $commit but one or the other part of the rest of the thing. It might take two lines using a single-line if and single-line else statement but I'd rather do it that way.

if ($commit) $returnval = @OCIExecute($insert, OCI_COMMIT_ON_SUCCESS);
else $returnval = @OCIExecute($insert, OCI_DEFAULT);
OR
if ($commit) $oci_flag = OCI_COMMIT_ON_SUCCESS;
else $oci_flag = OCI_DEFAULT;
$returnval = @OCIExecute($insert, $oci_flag);
OR even (I don't much care for this one, but... *shrug*)
$oci_flag = ($commit) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT;
$returnval = @OCIExecute($insert, $oci_flag);

Embedding commands and actions, while reducing the lines of code, makes it harder to follow the logic without reading the entire line of every line. I think debugging is made easier if you can just look at the comments or at the first part of a line to know what it's doing and that it doesn't have anything to do with what's going wrong. There are times when you have to read the entire line because something seemingly unrelated to the problem is causing the problem, but I've found most of the time that's not the case.

I would rather scroll past twenty lines of clearly marked code than try to decypher five lines of clever code.

*shrug* Sorry, I'm rambling now. Someone in Imperial Kingdoms has been harrassing me about not answering a 'simple question' like how many lines of code the game has. I finally told them to take it or leave it because I'm not about to start keeping track of the number of lines in the code.

swanhart From: swanhart Date: July 28th, 2005 09:29 pm (UTC) (Link)
From a performance perspectice, your code uses a bit more CPU and memory because values need to be copied.

if ( (empty($stmt) ? false : ocierror($stmt)) ||
ocierror($this->_DB_Link) )


|| operators can't short circuit, so the ocierror() is always called

OOP goes from right to left, so
empty($stmt) is evalated
the ternary is evaluated
if true then false is "returned" by the ternary
if false then ocierror($stmt) is "returned" by the ternary
then the ocierror($this->_DB_Link) is evaluated
and the result is compared with the result on the other half of the OR (which is either false or the ocierror() return value)

the "long" way would be:

if (!empty($stmt)) $res1 = ocierror($stmt); else $res1 = false;
if ( $res1 || ocierror($this->DB_Link) ) {
...
swanhart From: swanhart Date: July 28th, 2005 06:40 pm (UTC) (Link)
also, nice to see somebody using the overflow style. Most people don't use it.

I personally like it for pretty data tables...

Col1Col2
data1data2
data1data2
data1data2
data1data2
data1data2
data1data2
data1data2
data1data2
data1data2
data1data2
zimzat From: zimzat Date: July 28th, 2005 08:31 pm (UTC) (Link)

Some of the minor things that CSS can do are great, but I'm about to rip its head off for major design issues. Something I know exactly how to do under the 'old' (thus 'bad') way is causing me hours of headache in CSS while having very little success, and this is in Firefox!

For instance, I need a DIV to size its width to the size of its content, yet unless its floating it always takes up 100% of the page.

I also need to create a a four-column, four-row table-like display of variables and their values. I've tried using a couple of divs and floating left and right and then putting more floating divs left and right instead of those but they push eachother off the same row so it ends up a list of two columns and eight rows. Whenever I do get them on the same row its by adding a width: 48%; clause, but if one var is too long they're unaligned again. If I did it in tables this is what I'd look like and I could have it done in no time.

Var: Val   Var: Val
Var: Val   Var: Val
Var: Val   Var: Val
Var: Val   Var: Val
swanhart From: swanhart Date: July 28th, 2005 10:02 pm (UTC) (Link)
try this:...

<style>
.table {
 width: 200;
 border: 1px solid black;
}

.table .td {
 float: left;
 width: 23%;
}

.table .td2 {
 float: left;
 text-align:right;
 width: 23%;
 margin-right:4px;
}
</style>

<div class="table" id="pseudoTable">
  <div class="tr">
    <div class="td"><b>Var:</b></div>
    <div class="td2">Val</div>
    <div class="td"><b>Var:</b></div>
    <div class="td2">Val</div>
    <div style="clear: both;"></div>
  </div>
  <div class="tr">
    <div class="td"><b>Var:</b></div>
    <div class="td2">Val</div>
    <div class="td"><b>Var:</b></div>
    <div class="td2">Val</div>
    <div style="clear: both;"></div>
  </div>
  <div class="tr">
    <div class="td"><b>Var:</b></div>
    <div class="td2">Val</div>
    <div class="td"><b>Var:</b></div>
    <div class="td2">Val</div>
    <div style="clear: both;"></div>
  </div>
  <div class="tr">
    <div class="td"><b>Var:</b></div>
    <div class="td2">Val</div>
    <div class="td"><b>Var:</b></div>
    <div class="td2">Val</div>
    <div style="clear: both;"></div>
  </div>
</div>
zimzat From: zimzat Date: July 29th, 2005 12:44 am (UTC) (Link)
It works and looks very nice with the default values, but... with a var name longer than three or four letters, and that has a space, it goes a little haywire. If I do something as simple as remove the border from the 'table' div it goes completely haywire, putting values on the next line before the rest of the var/val pairs, and what-not.
11 comments or Leave a comment