views:

2610

answers:

10

If I'm deep in a nest of loops I'm wondering which of these is more efficient:

if (!isset($array[$key])) $array[$key] = $val;

or

$array[$key] = $val;

The second form is much more desirable as far as readable code goes. In reality the names are longer and the array is multidimensional. So the first form ends up looking pretty gnarly in my program.

But I'm wondering if the second form might be slower. Since the code is in one of the most frequently-executed functions in the program, I'd like to use the faster form.

Generally speaking this code will execute many times with the same value of "$key". So in most cases $array[$key] will already be set, and the isset() will return FALSE.

To clarify for those who fear that I'm treating non-identical code as if it were identical: as far as this part of the program is concerned, $val is a constant. It isn't known until run-time, but it's set earlier in the program and doesn't change here. So both forms produce the same result. And this is the most convenient place to get at $val.

+1  A: 

The overhead of a comparison which may or may not be true seems like it should take longer.

What does running the script in both configurations show for performance time?

warren
A: 

The extra function call to isset() is almost guaranteed to have more overhead than any assignment. I would be extremely surprised if the second form is not faster.

Jay
The PHP manual says that "isset()" is a "language construct", not a function. So I expect the overhead might actually be minimal. Guess I'll have to profile it and see.
Ben Dunlap
+2  A: 

isset() is very fast with ordinary variables, but you have an array here. The hash-map algorithm for arrays is quick, but it's still takes more time than doing nothing.

Now, first form can be faster if you have more values that are set, than those that are not, simply because it just looks up for hash without fetching or setting the value. So, that could be a point of difference: pick the first form if you have more 'hits' at keys that are set, and pick the second one if you have more 'misses'.

Please note that those two pieces of code are not identical. The first form will not set the value for some key when it's already set - it prevents 'overwriting'.

Milan Babuškov
The two pieces of code are effectively identical if $val is a constant. For my purposes it is. Edited my question to clarify this; thanks.
Ben Dunlap
Not sure I understand your answer, though:"pick the second one if you have more 'hits' at a key that is already set".Are you saying that if $array[$key] will already be set more often than not, I should *not* call isset()?
Ben Dunlap
@bslorence - you're correct, I fixed it now.
Milan Babuškov
A: 

I'm not sure the two forms are equivalent. Can it ever be the case that $array[$key] has a different value than the current value of $val? If so, the second form updates it but the first does not.

Aside from that, it seems that the relative speed of the two forms would depend on the percentage of keys that need to be assigned. If you're doing comparisons on all but updating, say, 1%, the first form may be faster. If you need to assign values to 98% of them, you're probably better off without the comparison.

But the only way to know for sure is to profile it.

Bruce Alderman
+2  A: 

Have you measured how often you run into the situation that $array[$key] is set before you try to set it? I think one cannot give a general advice on this, because if there are actually a lot of those cases, the isset check could possibly save some time by avoiding unnessecary sets on the array. However, if this is just rarely the case, the overhead could slow you down …. The best thing would be to do a benchmark on your actual code.

However, be aware that both codes can lead to different results! If $val is not always the same for a $array[$key] combination, the former code would always set the value to the first $val for that $array[$key] where the latter code would always set it to the last value of that combination.

(I guess you are aware of that and $val is always the same for $array[$key], but some reader stopping by might not.)

hangy
A: 

Do you need an actual check to see if the key is there? With an assignment to a blank array the isset() will just slow the loop down. And unless you do a second pass with data manipulation I strongly advise against the isset check. This is population, not manipulation.

The Wicked Flea
+5  A: 

For an array you actually want: array_key_exists($key, $array) instead of isset($array[$key]).

Zan Lynx
isset() is actually much faster. See comments on array_key_exists page in PHP online manual.
Milan Babuškov
Apparently isset() can be misleading, though, because it returns FALSE if $array[$key] == null:http://us3.php.net/manual/en/function.array-key-exists.php#83500Doesn't affect my situation but it's worth knowing about.
Ben Dunlap
Not if you think about it, the value isn't set if it equals null, but the array key still exists. If you're deleting something from an array, you should use unset() to avoid this issue.
R. Bemrose
A: 

i am a newbie to PHP but a combination of both could be with the ternary operator

$array[$key] = !isset($array[$key]) ? $val : $array[$key];

that's one way to go with it.

Tony L.
Read up on the ternary operator a bit; you're missing part of it in your sample. ;-)
Ben Dunlap
my bad :P edited
Tony L.
A: 

You can take a look at the PHP source code to see the difference. Didn't check whether this would be different in later versions of PHP, but it would seem in PHP3 the associative array functionality is in php3/php3_hash.c.

In the function _php3_hash_exists, the following things are done:

  • key is hashed
  • correct bucket found
  • bucket walked, until correct item found or not

Function _php3_hash_add_or_update:

  • hashed
  • bucket found
  • walked, existing overridden if existed
    • if didn't exist, new one added

Therefore it would seem just setting it is faster, because there is just one function call and this hashing and bucket finding business will only get done once.

Bemmu
+1  A: 

You should check the array upto but not including the level you are going to set.

If you're going to set

$anArray[ 'level1' ][ 'level2' ][ 'level3' ] = ...

You should make sure that the path upto level2 actually exists prior to setting level3.

$anArray[ 'level1' ][ 'level2' ]

No puppies will actually be killed if you don't, but they might be annoyed depending on your particular environment.

You don't have to check the index you are actually setting, because setting it automatically means it is declared, but in the interest of good practice you should make sure nothing is magically created.

There is an easy way to do this:

<?php

function create_array_path( $path, & $inArray )
{
    if ( ! is_array( $inArray ) )
    {
     throw new Exception( 'The second argument is not an array!' );
    }
    $traversed = array();
    $current = &$inArray;

    foreach( $path as $subpath )
    {
     $traversed[] = $subpath;
     if ( ! is_array( $current ) )
     {
      $current = array();
     }
     if ( ! array_key_exists( $subpath, $current ) )
     {
      $current[ $subpath ] = '';
     }
     $current = &$current[ $subpath ];
    }
}


$myArray = array();

create_array_path( array( 'level1', 'level2', 'level3' ), $myArray );

print_r( $myArray );

?>

This will output:

 Array
 (
     [level1] => Array
         (
             [level2] => Array
                 (
                     [level3] => 
                 )

         )

 )
Kris