Ask a Jedi: Why is one UDF faster than another? Variables?

This post is more than 2 years old.

Patrick Stormthunder asks:

After some testing on my company's site for the purpose of optimization, I came across something that has shaken my faith in, well, variables. I am hoping you can help me understand these results.

What follows is a simple implementation of a "null" value with the test for null (isNull()) written two ways, the first with a variable reference and the second without one. Running the first test 1000 times takes 7000 milliseconds on my development machine. Running the second test takes 25 milliseconds.

Before showing Patrick's code, I'll say right away that I'm not a big fan of the 'run this code block 1 zillion times' speed test. I don't think it's very reliable or realistic. Yes, I've done it myself, but at the end of the day I think you can do better. As an example of why I don't trust speed tests, when I ran his code, my results were incredibly different. The slow code took, on average, 30ms, while the fast code took about 8ms. At that level, I'd simply not worry about it and use what is more appropriate/better written/etc. Let's take a quick look at his code though: cfset variables.null="|\| |_| |_ |_"/>

<cffunction name="isNull" hint="is the valueless?" access="public" output="false"> <cfargument name="value" required="true" /> <cfreturn arguments.value EQ variables.null /> </cffunction>

<cftimer label="test1"> <cfloop index="i" from="0" to="1000"> <cfset myTestVar=isNull(i) /> </cfloop> </cftimer>

<cfset variables.null="|| || | |_"/>

<cffunction name="isNull2" hint="is the valueless?" access="public" output="false"> <cfargument name="value" required="true" /> <cfreturn arguments.value EQ "|| || | |_" /> </cffunction>

<cftimer label="test2"> <cfloop index="i" from="0" to="1000"> <cfset myTestVar=isNull2(i) /> </cfloop> </cftimer>

As you can see - he has two UDFs. Each takes a value and compares it to a null variable which is a string. The first UDF is the slow one - but as I said, even though my test showed it to be about 3 times as slow, it was still around 30 ms. What bugs me though and what I think is more important is the break of proper encapsulation. The UDF directly accesses the outside Variables scope, and shoot, perhaps that is the reason for the slow down. Either way, even if the speed tests were reversed, I'd recommend the second UDF simply because it is better written and more encapsulated.

Oh a whim I went ahead and added a third test:

<cftimer label="test3"> <cfloop index="i" from="0" to="1000"> <cfset someres = (i eq variables.null)> </cfloop> </cftimer>

This returned results right in the middle of the two UDFs. I then wrote one more test:

<cftimer label="test4"> <cfloop index="i" from="0" to="1000"> <cfset someres = compare(i, variables.null) is 0> </cfloop> </cftimer>

This code block ran the fastest, although only slightly more than the second UDF, and I'm sure if I used compare in the UDF it would catch up. But you know what? I hate compare. I can never remember it's API so at the end of day, I'll use EQ just to make things more readable.

p.s. I'll use this blog entry as another excuse to recommend ColdFire. Check out how nicely displayed the timer results are shown at the bottom my browser:

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA