Menu

As simple as possible, as complex as necessary

Simpler and (ever so) slightly faster looping in Lucee cfscript

18 February 2016

The ability to loop a given number of times is a basic programming concept and for the last two decades, writing first javascript and then CFML script, I've largely been using the familiar "C-style" syntax.

for( i=1; i <= 10; i++ ){
 currentValue=i;
}

Not intuitive

Despite regularly using the construct, I admit it was quite a while before I grasped what all the algebra-like stuff inside the parentheses actually did. Logical of course, but not particularly intuitive.

As CFML has evolved and especially since we moved over to Lucee from ColdFusion 9, I find I rarely need this particular type of for-loop any more. In most cases I'm iterating over an array, query or struct rather than needing to loop a certain number of times and the language now allows you to do this without needing to handle the increments: using "for-in" loops or the (relatively) new iteration functions.

Still, I do have a few of those C-style for-loops dotted around my code base where I just want to iterate a set number of times.

Tag in script

From now on though, I plan to ditch them in favour of the following:

loop from=1 to=10 index="i"{
  currentValue=i;
}

Some dislike the whole "tags-in-script" implementation, but not only is this to my eyes clearer and more expressive than all those semi-colons, angle brackets and plus signs, according to Gert it's also a little bit faster in the current version of Lucee (4.5.2.018).

Putting this to the test:

struct function loopTest( method, iterations ){
	var milliseconds=0;
	stopwatch variable="milliseconds"{
		switch( method ){
			case "for":
				for( var i=1; i <= iterations; i++ ){
					// do something
				}
			break;
			case "loopFromTo":
				loop from=1 to=iterations index="local.i"{
					// do something
				}
			break;
		}
	}
	var result={};
	result[ method ]=milliseconds & "ms";
	return result;
}
iterations=100000;//one hundred thousand
Dump( loopTest( "for",iterations ) );
Dump( loopTest( "loopFromTo",iterations ) );

(Side note: I've used local.i to scope the index variable in the "loopFromTo" version even though I normally prefer using the var keyword to declare local vars. In this case I think it reads better and subsequent references to i don't need to be scoped).

Executing the above code a dozen times to eliminate compilation delays, the sort of results I'm seeing on my fairly beefy dev machine are:

for15ms
loopFromTo0ms

That is not significant. Even with a million loops I'm only seeing an approximate difference of 100-150ms.

But I'll take it along with the far more important clarity it offers.

Times-are-a-changing

As Gert notes, Lucee 5 will add a new option for simple "times" looping where no index reference is needed. I haven't tested it yet but it promises to be fast—and very clear.

loop times=100000{
  // do something (and make it snappy)
}

Posted on . Updated

Comments

  • Formatting comments: See this list of formatting tags you can use in your comments.
  • Want to paste code? Enclose within <pre><code> tags for syntax higlighting and better formatting and if possible use script. If your code includes "self-closing" tags, such as <cfargument>, you must add an explicit closing tag, otherwise it is likely to be mangled by the Disqus parser.
Back to the top