Jump to content

First iteration of foreach much slower than rest


lonewolf217

Recommended Posts

Specific question with general application here. i am having some trouble with a loop and am getting some odd timing results

specifically this is a WMI query on a hyper-v server retrieving disk information but im not sure the context of the query is relevant here. The main question is how come the first iteration of the loop is so much slower than the other iterations, it just doesnt seem to make sense to me unless ive done something stupid here i cannot see.  any ideas ?

 

<?php
$diskstart = microtime(true);
// get the hard disk size 
try {
$resultDisk = $wmi->ExecQuery("SELECT BlockSize,NumberOfBlocks FROM msvm_LogicalDisk where systemname like '%" . $guid . "%' and name='Hard Disk Image'");
$ct=0;
$start1 = microtime(true);
$start = microtime(true);
foreach($resultDisk as $itemDisk) {
	$end = microtime(true);
	echo "<BR>FOREACH: [" . Round($end-$start,3) . "] seconds";
	$vm[$name]['disks'][$ct] = ($itemDisk->BlockSize * $itemDisk->NumberOfBlocks)/1024;  // format the disk size into KB
	$ct++;
	$start = microtime(true);
}
$end1 = microtime(true);
echo "<br>      ALLLOOP: [" . Round($end1-$start1,3) . "] ";
}
catch(exception $e) {
}
$diskend = microtime(true);
echo "<br>DISK: [" . ($diskend-$diskstart) . "] seconds";
?>

 

 

and here are the results

FOREACH: [1.236] seconds
FOREACH: [0.001] seconds
FOREACH: [0.001] seconds
FOREACH: [0.001] seconds
FOREACH: [0.002] seconds
FOREACH: [0.001] seconds
FOREACH: [0.001] seconds
FOREACH: [0.001] seconds
FOREACH: [0.001] seconds
FOREACH: [0.001] seconds
FOREACH: [0.001] seconds
ALLLOOP: [2]
DISK: [2.1837468147278] seconds

Link to comment
Share on other sites

I may be wrong but I remember hearing or reading somewhere that a foreach loop makes an identical copy of the array, and then works on that copy leaving the original intact. If that is right, then what you are seeing is the delay to create the copy.

 

Hope that helps,

Joe

Link to comment
Share on other sites

Not one of the ones i saw before but still an interesting read.  I did try to use this

 

<?php
$x = &$resultDisk;
ForEach($x as $itemDisk) {
?>

 

but it didn't seem to improve the speed at all. I will give your article a closer looksee to make sure i didn't miss anything.

Link to comment
Share on other sites

I may be wrong but I remember hearing or reading somewhere that a foreach loop makes an identical copy of the array, and then works on that copy leaving the original intact.

You are wrong. Read it http://ru2.php.net/manual/en/control-structures.foreach.php Pay attention on "When foreach first starts executing, the internal array pointer is automatically reset to the first element of the array." and other words. Foreach is iterating through an original array, it just change an internal pointer (current position) of that array.

Link to comment
Share on other sites

I may be wrong but I remember hearing or reading somewhere that a foreach loop makes an identical copy of the array, and then works on that copy leaving the original intact.

You are wrong. Read it http://ru2.php.net/manual/en/control-structures.foreach.php Pay attention on "When foreach first starts executing, the internal array pointer is automatically reset to the first element of the array." and other words. Foreach is iterating through an original array, it just change an internal pointer (current position) of that array.

Try again.  Foreach makes a copy of an array.  I demonstrated that in the article I wrote. 

 

The quote you're using demonstrates that calling foreach will always start at the first element.  It has nothing to do with copies.

Link to comment
Share on other sites

The quote you're using demonstrates that calling foreach will always start at the first element.  It has nothing to do with copies.

Did you read in the help "As foreach relies on the internal array pointer changing it within the loop may lead to unexpected behavior." and "In order to be able to directly modify array elements within the loop precede $value with &. In that case the value will be assigned by reference."? In the article you talked about this rule is broken. It means that all your research is based on a broken rule and so can't be true :)

 

Sorry, I believe PHP developers but not you. They say that foreach() makes a copy of every element at each iteration but not a copy of the whole array - as you say. You've written an article (it's nice that you've done it!!!) - but it based on a broken rule. You'd better rewrite it.

Link to comment
Share on other sites

You are still misunderstanding those sentences.  An internal copy of the array is absolutely made prior to the foreach.  Look:

 

 

php > $arr = array('a', 'b', 'c');
php > foreach ( $arr as $letter ) {
php {   $arr[] = strtoupper($letter);
php { }
php > print_r($arr);
Array
(
    [0] => a
    [1] => b
    [2] => c
    [3] => A
    [4] => B
    [5] => C
)

Now if, as you said, no copy was made of the ENTIRE array, then as I added items to the end of the array within the loop, that would continually increase the size of the array.  If, as you think, no copy of the whole array is made, then when the pointer got to element 3, it would see 'A', and add 'A' in as element 6 and on and on forever. 

 

However, as I've said twice now (and wrote a well-researched article about with examples you could have tried), when a foreach loop is started on an array with a refcount of 1 (no references to it other than the variable name in use), a complete copy of that array is created and used as the control for the loop.  modifications to what looks like the original array will have no effect on the operation of the loop.

 

In addition to that, the quotes from the manual ALSO apply.  If you modify the array pointer inside the loop, the loop will malfunction in unpredictable ways. 

 

Now, if you still don't believe me even after reading the proof in my article and the proof I just pasted above, there's not much hope for you.  Both my article and the PHP manual are correct, they're talking about different things.

Link to comment
Share on other sites

No. I don't agree with you. The only thing that is copied - values of every array's element, but not the whole array!!!

 

Have a look at this loop:

$arr = array('a', 'b', 'c', 'd', 'e', 'f');
foreach ( $arr as $k => $value ) 
{
  if( $k == 1 ) $arr[1] = 'newval';
}

echo '<pre>'. print_r($arr, true). '</pre>';

What do we get? This array:

Array
(
    [0] => a
    [1] => newval
    [2] => c
    [3] => d
    [4] => e
    [5] => f
)

It means that we change an original array, but not any ghost "local copy". Yes, $value represents a copy of one array's element at every iteration. If you break a loop we will see it (BTW according PHP help).

 

Just an example

$arr = array('a', 'b', 'c', 'd', 'e', 'f');
foreach ( $arr as $k => $value ) 
{
//  if( $k == 1 ) $arr[1] = 'newval';
  if( $k == 3 ) break;
}

echo $value;

The result is letter 'd'. It means that letters a, b, c and d were copied to $value at different iteration. But (AGAIN!) $arr is still the same array.

 

An once more. In your article you sometimes break some rules concerning PHP arrays. Then you interpret it and make a wrong decision. In short - it's wrong because based on a wrong suggestions.

 

When you send an array into function - yes, local copy is made! You may change an array inside your function but an original array is not changed. But here, in foreach loop, we see quite another situation.

 

PS. You may still believe to your article... It can't prevent somebody to make PHP scripts :) But you'd better understand it and re-write your article for your better self-esteem.

Link to comment
Share on other sites

The copied array is only used internally by PHP.  Maybe that's where the misunderstanding is coming from.

 

You try to loop over $arr.  PHP creates $arr_copy, and uses that internally to control the loop. You cannot access the copied array.  This is not like a function call, it's a behind the scenes copy.  It's not a local or lexical copy, it's a control copy, used by the language and not by you.

 

The post I put above proves that SOMETHING stops the array from going into an infinite loop.  How would PHP know the original length of the array without a starting-point copy? 

 

You can also create large memory-intensive arrays (like object trees) and use memory_get_usage to see the large spike in memory when you start a foreach.

 

Your second example has nothing to do with the topic.  Values created inside of a foreach are still in scope when the loop exits. That's just how PHP's scope works, it's not broken by loops.

 

If you continue to tell me I'd better understand the topic you're clearly not understanding, you're going to continue to look foolish.  Stop with the personal insults and address the topic.

 

An once more. In your article you sometimes break some rules concerning PHP arrays.
What rules?  What "rule of arrays" did I break in my article?  You do understand that some of the demonstrations in my article were made to show exactly the behavior I've been describing right? 

 

 

Link to comment
Share on other sites

Have a look at this loop:

[..snip..]

It means that we change an original array, but not any ghost "local copy". Yes, $value represents a copy of one array's element at every iteration. If you break a loop we will see it (BTW according PHP help).

 

Of course your changing the original array, your specifically referencing the original array in your assignment statement.  This shows nothing.

 

 

Just an example

[..snip..]

The result is letter 'd'. It means that letters a, b, c and d were copied to $value at different iteration. But (AGAIN!) $arr is still the same array.

 

This also shows nothing.  Your just outputting the $value variable, and not doing anything with the array.

 

For a normal foreach, without references, php does make a copy of the array.  It's an internal copy however, and you as the script developer have absolutely no way to access it.  PHP uses it to control the iteration process, which is why adding or removing elements from the array does not affect how many iterations the loop runs.  If you choose to use $value as a reference however, php will reference the original array during it's iteration process.

 

For example:

<?php
header('Content-type: text/plain');

$arr = range('a', 'd');

$len=count($arr);
foreach ($arr as $key=>$val){
echo "{$key} => {$val}\r\n";
unset($arr[--$len]);
}

print_r($arr);

 

If PHP referenced original array for controlling it's iterations, the above would only iterate twice showing elements a and b.  c and d would have been deleted during the first two iterations.

 

Another example:

<?php
header('Content-type: text/plain');

$arr = range('a', 'd');

$len=count($arr);
foreach ($arr as $key=>$val){
echo "{$key} => {$val}\r\n";
$arr[--$len]=rand();
}

print_r($arr);

 

Notice how even through we are changing the values of what would be c and d during the first two iterations, they still appear as 'c' and 'd' in the loop.  This shows they are being assigned from the internal copy.

 

If we do this instead how ever (use a reference):

<?php
header('Content-type: text/plain');

$arr = range('a', 'd');

$len=count($arr);
foreach ($arr as $key=>$val){
echo "{$key} => {$val}\r\n";
$arr[--$len]=rand();
}

print_r($arr);

 

We can see that PHP does reference the original array when assigning the values.

 

Finally,

<?php
header('Content-type: text/plain');

$arr = range('a', 'd');

$len=count($arr);
foreach ($arr as $key=>&$val){
echo "{$key} => {$val}\r\n";
unset($arr[--$len]);
}

print_r($arr);

 

shows that it also uses the original array to control the number of iterations the loop does as we only see 'a' and 'b'.

 

An once more. In your article you sometimes break some rules concerning PHP arrays. Then you interpret it and make a wrong decision. In short - it's wrong because based on a wrong suggestions.

 

The above examples do not break any rules regarding arrays or foreach loops.

 

Link to comment
Share on other sites

If you continue to tell me I'd better understand the topic you're clearly not understanding, you're going to continue to look foolish.

You may think anything that you like... OK, let's stop this discussion. In any case your personal opinion will not interfere my programming :)

But, in any case, read help about foreach() once more and once more. It tells many words about internal pointer of original array, how is it changed during a loop.

Link to comment
Share on other sites

But, in any case, read help about foreach() once more and once more. It tells many words about internal pointer of original array, how is it changed during a loop.

 

It's reset to the first element.  That is all though (unless you use a reference value).

 

$arr = range('a', 'g');
foreach ($arr as $key=>$val){
echo "{$key} => {$val}\r\n";
var_dump(key($arr));
}

 

key() shows you which index the internal pointer is currently at.  As you can see if you run the above code, it does not change as the loop progresses.

 

Link to comment
Share on other sites

But, in any case, read help about foreach() once more and once more. It tells many words about internal pointer of original array, how is it changed during a loop.

It says no such thing, and you have been proven wrong three times now.  I'm closing this thread so your misinformation doesn't continue to pop up to the top of the forum.  You are wrong, both kicken and I have demonstrated that.  I know English isn't your first language, so trust me when I tell you that you are misinterpreting the manual. 

 

Thread closed. 

Link to comment
Share on other sites

Guest
This topic is now closed to further replies.
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.