lonewolf217 Posted January 28, 2012 Share Posted January 28, 2012 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 https://forums.phpfreaks.com/topic/255937-first-iteration-of-foreach-much-slower-than-rest/ Share on other sites More sharing options...
joe92 Posted January 28, 2012 Share Posted January 28, 2012 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 https://forums.phpfreaks.com/topic/255937-first-iteration-of-foreach-much-slower-than-rest/#findComment-1311973 Share on other sites More sharing options...
lonewolf217 Posted January 28, 2012 Author Share Posted January 28, 2012 i have read similar online, i just cannot understand why this wmi class takes so much longer than some of the others i am querying. Oh well, ill see if there is another way to improve it or try the same thing in vbscript to accomplish what i need. Link to comment https://forums.phpfreaks.com/topic/255937-first-iteration-of-foreach-much-slower-than-rest/#findComment-1311975 Share on other sites More sharing options...
ManiacDan Posted January 28, 2012 Share Posted January 28, 2012 You may have read about it in my article on it here. There are some tricks in that article (like assigning a fake reference to the variable) to cut down on the copy size and memory usage. -Dan Link to comment https://forums.phpfreaks.com/topic/255937-first-iteration-of-foreach-much-slower-than-rest/#findComment-1311990 Share on other sites More sharing options...
lonewolf217 Posted January 28, 2012 Author Share Posted January 28, 2012 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 https://forums.phpfreaks.com/topic/255937-first-iteration-of-foreach-much-slower-than-rest/#findComment-1311994 Share on other sites More sharing options...
ManiacDan Posted January 28, 2012 Share Posted January 28, 2012 Is $resultDisk an array or an object that implements arrayAccess? That's a completely different thing. if it's an object, there's nothing you can do really. Link to comment https://forums.phpfreaks.com/topic/255937-first-iteration-of-foreach-much-slower-than-rest/#findComment-1312004 Share on other sites More sharing options...
lonewolf217 Posted January 28, 2012 Author Share Posted January 28, 2012 unfortunately wmi calls seem to return variant objects which are a pain in the ass to work with. so i guess there is nothing i can do about it. Link to comment https://forums.phpfreaks.com/topic/255937-first-iteration-of-foreach-much-slower-than-rest/#findComment-1312005 Share on other sites More sharing options...
SergeiSS Posted January 28, 2012 Share Posted January 28, 2012 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 https://forums.phpfreaks.com/topic/255937-first-iteration-of-foreach-much-slower-than-rest/#findComment-1312013 Share on other sites More sharing options...
ManiacDan Posted January 28, 2012 Share Posted January 28, 2012 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 https://forums.phpfreaks.com/topic/255937-first-iteration-of-foreach-much-slower-than-rest/#findComment-1312016 Share on other sites More sharing options...
SergeiSS Posted January 28, 2012 Share Posted January 28, 2012 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 https://forums.phpfreaks.com/topic/255937-first-iteration-of-foreach-much-slower-than-rest/#findComment-1312026 Share on other sites More sharing options...
ManiacDan Posted January 28, 2012 Share Posted January 28, 2012 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 https://forums.phpfreaks.com/topic/255937-first-iteration-of-foreach-much-slower-than-rest/#findComment-1312051 Share on other sites More sharing options...
SergeiSS Posted January 28, 2012 Share Posted January 28, 2012 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 https://forums.phpfreaks.com/topic/255937-first-iteration-of-foreach-much-slower-than-rest/#findComment-1312075 Share on other sites More sharing options...
ManiacDan Posted January 29, 2012 Share Posted January 29, 2012 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 https://forums.phpfreaks.com/topic/255937-first-iteration-of-foreach-much-slower-than-rest/#findComment-1312083 Share on other sites More sharing options...
kicken Posted January 29, 2012 Share Posted January 29, 2012 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 https://forums.phpfreaks.com/topic/255937-first-iteration-of-foreach-much-slower-than-rest/#findComment-1312087 Share on other sites More sharing options...
SergeiSS Posted January 29, 2012 Share Posted January 29, 2012 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 https://forums.phpfreaks.com/topic/255937-first-iteration-of-foreach-much-slower-than-rest/#findComment-1312128 Share on other sites More sharing options...
kicken Posted January 29, 2012 Share Posted January 29, 2012 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 https://forums.phpfreaks.com/topic/255937-first-iteration-of-foreach-much-slower-than-rest/#findComment-1312133 Share on other sites More sharing options...
ManiacDan Posted January 29, 2012 Share Posted January 29, 2012 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 https://forums.phpfreaks.com/topic/255937-first-iteration-of-foreach-much-slower-than-rest/#findComment-1312166 Share on other sites More sharing options...
Recommended Posts