Archive for August, 2010

Looping over Arrays

Wednesday, August 25th, 2010

I’ve been working in CF 7 a lot lately.  This has forced me to look at writing Coldfusion differently from what I’m used to, being Coldfusion 8 & 9.  An example is that in CF 7 there is no attribute available in the <cfloop> tag.  This means that I have to loop over an array by looping from 1 to however long the array is.  There are two ways to loop over an array in CF 7.  First is to evaluate the array length with each loop, and the second is to set the length of the loop into a variable and loop from 1 to that value.

To illustrate the first example of evaluating with each loop the code would look like:

<cfloop from="1" to="#arrayLen(variables.myArray)#" index="variables.i">

</cfloop>

and the to illustrate the second example of setting the length first the code would look like:

<!-- set the length of the array -->
<cfset variables.aryLen = arrayLen(variables.myArray) />

<cfloop from="1" to="#variables.aryLen#" index="variables.i">

</cfloop>

Lastly, in Coldfusion 8 & 9 you can write it like the following:

<cfloop array="#variables.myArray#" index="variables.i">

</cfloop>

So of the first two options, clearly setting the length of the array into a variable has design advantages and should always be the preferred method. Nonetheless I was curious if there was really a performance difference.

I used the following script to loop over an array with 100,000 values using the three above methods.

<cfif NOT structKeyExists(SESSION, "asAttribute")>
	<cfset SESSION.asAttribute = arrayNew(1) />
</cfif>

<cfif NOT structKeyExists(SESSION, "setLength")>
	<cfset SESSION.setLength = arrayNew(1) />
</cfif>

<cfif NOT structKeyExists(SESSION, "evaluateLength")>
	<cfset SESSION.evaluateLength = arrayNew(1) />
</cfif>

<cfif NOT structKeyExists(SESSION, "refreshes")>
	<cfset SESSION.refreshes = 0 />
</cfif>

<cfset SESSION.refreshes = SESSION.refreshes + 1 />

<cfoutput>
	Number of refreshes: #SESSION.refreshes#<br /><br />
</cfoutput>

<!-- create an array -->
<cfset variables.myArray = arrayNew(1) />

<!-- populate array -->
<cfloop from="1" to="100000" index="variables.i">
	<cfset variables.myArray[variables.i] = "Value#variables.i#" />
</cfloop>

<cfset variables.startCount = getTickCount() />

<cfloop array="#variables.myArray#" index="variables.i">

</cfloop>

<cfset variables.endCount = getTickCount() />

<cfset variables.processingTime = variables.endCount - variables.startCount />

<cfset arrayAppend(SESSION.asAttribute, variables.processingTime) />

<cfset variables.attributeAvg = arrayAvg(SESSION.asAttribute) />

<cfoutput>
	Using as an attribute - <br />
	Average: #variables.attributeAvg#
</cfoutput>

<br /><br /><br />

<!-- set the length of the array -->
<cfset variables.aryLen = arrayLen(variables.myArray) />

<cfset variables.startCount = getTickCount() />

<cfloop from="1" to="#variables.aryLen#" index="variables.i">

</cfloop>

<cfset variables.endCount = getTickCount() />

<cfset variables.processingTime = variables.endCount - variables.startCount />

<cfset arrayAppend(SESSION.setLength, variables.processingTime) />

<cfset variables.attributeAvg = arrayAvg(SESSION.setLength) />

<cfoutput>
	Setting length in a varible - <br />
	Average: #variables.attributeAvg#
</cfoutput>

<br /><br /><br />

<cfset variables.startCount = getTickCount() />

<cfloop from="1" to="#arrayLen(variables.myArray)#" index="variables.i">

</cfloop>

<cfset variables.endCount = getTickCount() />

<cfset variables.processingTime = variables.endCount - variables.startCount />

<cfset arrayAppend(SESSION.evaluateLength, variables.processingTime) />

<cfset variables.attributeAvg = arrayAvg(SESSION.evaluateLength) />

<cfoutput>
	Evaluating length for each loop - <br />
	Average: #variables.attributeAvg#
</cfoutput>

Each was timed and stored in the session. At the end of 10, 25, 50, and 100 page refreshes the average execution speed was determined for each method.

After all was said and done evaluating the array for each loop vs. setting the length and looping to the length was within 4 milliseconds running a loop over an array with 100,000 values. The following chart illustrates the differences.

Array Looping Execution Comparison

Hopefully, what is obvious from this is not so much that there is little to no difference in the execution time between pre-setting the array length vs. evaluating it with each loop, but that using the array as an attribute (which can only be used in Coldfusion 8 & 9) of the tag runs much faster than either of the other two methods. About 25 milliseconds faster.