Array Looping Much Faster Than Query Looping

Posted September 10, 2006 at 10:31 AM by Ben Nadel

Tags: ColdFusion

I pretty much guessed that looping over an array in ColdFusion would be faster than looping over a query in ColdFusion. But, to me, looping over a query is SOOO much cleaner looking (only when using the CFLoop tag, not in CFScript tags) than looping over an array that I ran this test in hopes that magically a query loop would be faster. I was thinking about this in terms of creating static collections of data, such as a list of states or a list of countries. Who hasn't had to loop over those at some time?

The following test creates a one-dimensional array and query and just tests how fast it can be iterated over. A query can be looped over in a query loop OR in an index loop, so I tested both of those. But first, I created the data sets:

  • <!--- Set the data size. --->
  • <cfset intDataSize = 10000 />
  •  
  •  
  • <!--- Create a data array. --->
  • <cfset arrGirls = ArrayNew( 1 ) />
  •  
  • <!--- Resize the array. --->
  • <cfset ArrayResize( arrGirls, intDataSize ) />
  •  
  • <!--- Add data to the array. --->
  • <cfloop index="intI" from="1" to="#intDataSize#" step="1">
  •  
  • <!--- Set data item. --->
  • <cfset arrGirls[ intI ] = ListGetAt(
  • "Libby,Marry Kate,Ashley,Azure,Francis,Lori,Alex",
  • RandRange( 1, 7 )
  • ) />
  •  
  • </cfloop>
  •  
  •  
  • <!--- Create the data query. --->
  • <cfset qGirls = QueryNew( "name" ) />
  •  
  • <!--- Add rows to the query. --->
  • <cfset QueryAddRow( qGirls, intDataSize ) />
  •  
  • <!--- Add data to the array. --->
  • <cfloop index="intI" from="1" to="#intDataSize#" step="1">
  •  
  • <!--- Set data item. --->
  • <cfset qGirls[ "name" ][ intI ] = ListGetAt(
  • "Libby,Marry Kate,Ashley,Azure,Francis,Lori,Alex",
  • RandRange( 1, 7 )
  • ) />
  •  
  • </cfloop>

As you can see from above, I am creating a data set that is 10,000 items long. Then, I tested how long each type of loop would take. For the looping, all I did was output the data item value:

  • <!--- Test how long the array loop takes. --->
  • <cftimer label="Array Loop" type="outline">
  •  
  • <!--- Loop over array using an index loop. --->
  • <cfloop index="intI" from="1" to="#ArrayLen( arrGirls )#" step="1">
  • #arrGirls[ intI ]#
  • </cfloop>
  •  
  • </cftimer>
  •  
  •  
  • <!--- Test how long the query loop takes. --->
  • <cftimer label="Query Loop" type="outline">
  •  
  • <!--- Loop over the query. --->
  • <cfloop query="qGirls">
  • #qGirls.name#
  • </cfloop>
  •  
  • </cftimer>
  •  
  •  
  • <!--- Test how long the query loop takes when used like an array. --->
  • <cftimer label="Query Loop (as Array)" type="outline">
  •  
  • <!--- Loop over the query using an index loop. --->
  • <cfloop index="intI" from="1" to="#qGirls.RecordCount#" step="1">
  • #qGirls[ "name" ][ intI ]#
  • </cfloop>
  •  
  • </cftimer>

Not suprisingly, the array loop was MUCH faster than either of the query loops. The speed results on average where:

  • Array: 31ms - 90ms
  • Query Loop: 3140ms - 3234ms
  • Query Loop (as Array): 2344ms - 3109ms

As you can see, the array loop is like 50 times faster than the query loop. Keep in mind that this is over 10,000 iterations, but still! That IS much faster. Using the query as a multidimensional array ([column][row]) had some speed improvements, but was still tens of times slower than the array index.

So again, nothing special here. That was pretty much was what I expected (though not quite such a large performance difference). But, I blogged earlier about the array iterator, and was curious how that would perform against the query loop. The iterator loop still looks nicer to me.

  • <!--- Test how long the array loop takes with the iterator. --->
  • <cftimer label="Array Loop (as Iterator)" type="outline">
  •  
  • <!--- Get the array iterator. --->
  • <cfset objIterator = arrGirls.Iterator() />
  •  
  • <!--- Loop over the array while we have items. --->
  • <cfloop condition="objIterator.HasNext()">
  • #objIterator.Next()#
  • </cfloop>
  •  
  • </cftimer>

Suprisingly, even with the added method calls and iterative condition evaluation, the array iterator is still VERY fast. On average it performed as such:

Array Iterator: 78ms - 93ms

As you can see, it performs comparable to the array index loop, but at the slow end of the spectrum. It's still like 50 times faster than the query loop. To me, it just looks very elegant. The one issue I have with it is that you have to create the iterator before you go into the loop. This doesn't look as nice as just looping. So, in a select box output, I might not opt for the iterator (as I am very anal about how my code looks), but if I was in a CFScript tag, I would definitely choose the array iterator over the array index loop as the code looks nice, is more "readable," and is less bulky.



Reader Comments

May 18, 2009 at 9:40 AM // reply »
2 Comments

I'm getting an error from cfloop query="qGirls" and qGirls.name. I don't see where the qGirls has been created.


May 18, 2009 at 9:48 AM // reply »
10,743 Comments

@Jay,

qGirls is created manually in the first block of code.


Ed
Feb 5, 2010 at 12:06 PM // reply »
1 Comments

Hey Ben,
I truly enjoy your posts. You are one smart guy. One thing I am curious about. I know this is an older post but wouldn't the loop run even quicker if the ArrayLen was calced prior to the loop?

Something like:

<!--- Loop over array using an index loop. --->
<cfset var arrGirlsLength = ArrayLen( arrGirls )>
<cfloop index="intI" from="1" to="#arrGirlsLength#" step="1">
#arrGirls[ intI ]#
</cfloop>

Thanks for all your informative posts.


Feb 6, 2010 at 5:21 PM // reply »
10,743 Comments

@Ed,

I appreciate the kind words :) Our code is actually the same as far as evaluation goes. In the tag-based implementation, the TO attribute is only evaluated once. As such, getting the array length outside the tag is no different.

But, that is *only* because my version is tag-based. If you were writing CFScript, then absolutely, you are correct. This *would* be slower:

for (intI = 1 ; intI < arrayLen( arrGirls ) ; intI++)

In that case, as you are saying, getting the array length before the loop would be a much better choice.


Jan 5, 2012 at 4:13 AM // reply »
2 Comments

  • <cfoutput>
  • <cfset arr = ArrayNew(1)>
  •  
  • <cfloop from="1" to="1000" index="k">
  • <cfset ArrayAppend(arr, "This is my loop testing....")>
  • </cfloop>
  •  
  • <cftimer label="iterator Loop" type="outline">
  • <cfloop from="1" to="#ArrayLen(arr)#" index="i">
  • #arr[i]#<br/>
  • </cfloop>
  • </cftimer>
  •  
  • <cftimer label="Array Loop" type="outline">
  • <cfloop array="#arr#" index="i">
  • #i#<br/>
  • </cfloop>
  • </cftimer>
  •  
  • </cfoutput>
  •  
  • <cfabort>

Output: First loop takes 6ms, second takes 4ms

Conclusion: looping over iterator, is slower then looping over array, but you loose iterator variable if you really need it!


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 21, 2012 at 1:58 AM
Updated: Converting A ColdFusion Query To CSV Using QueryToCSV()
Hi Ben, why do you need to have so many double quotes when adding the field and field name to the row data? ----------------------------------------- <cfset LOCAL.RowData[ LOCAL.ColumnIndex ] = ... read »
AXL
May 21, 2012 at 1:24 AM
URL Rewriting And ColdFusion's WriteToBrowser Image Functionality (CFFileServlet)
@Mounir, Open your lower case URL Rewrite rule and add the following condition. Condition input: {REQUEST_URI} Check if input string: Does Not Match the Pattern Pattern: ^/CFFileServlet/_cf_ca ... read »
May 20, 2012 at 4:28 AM
Understanding The Complex And Circular Relationships Between Objects In JavaScript
@Will Vaughn I tried your javascript example but got this error:- foo.print is not a function ... read »
May 19, 2012 at 5:37 AM
A Graphical Explanation Of Javascript Closures In A jQuery Context
Thanks for this article, but I fear you missed an important point. If variables in the outer context change, these changes affect the inner anonymous functions as well. That means: if you change the ... read »
May 18, 2012 at 3:39 PM
Parsing CSV Data With An Input Stream And A Finite State Machine
Can you use file upload button with this? and read live? or does the file have to already be on the server saved? ... read »
May 18, 2012 at 1:06 AM
VIRGO (Aug. 23-Sept. 22): Dead On The Money!
A friend of mine and I were arguing about astrology and she told me that he believes in astrology. She hasn't provided me with any evidence that the belief makes any sense to me. She she been telling ... read »
May 17, 2012 at 11:32 PM
Using ColdFusion to Handle 404 Errors (Page Not Found) On Development Server
Very easy the configuration. I read a lot pages and I can't find the solution. I open the administrator and change this Administrator/server settings/Error Handlers/Missing Template Handler and p ... read »
May 17, 2012 at 3:13 PM
LOCAL Variables Scope Conflicts With ColdFusion Query of Queries
I never cease to be amazed that almost EVERY random CF issue I come across lands me on your site. Thank you for documenting your findings for the world. ... read »