WHENTO: Evaluate() and Iif()

Bert brought up a rather interesting scenario where he needed to force templates to be reloaded into the template cache, since he was generating bits of CFML code at runtime. I suggested that he might also want to take a look at the evaluate() function, since that would probably allow him to solve his problem in a simpler manner.

As has been noted often enough, evaluate() and iif() are not the most performant of functions, since they do need to dynamically compile the expressions that they’re being asked to process.

Evaluate() simply processes the string passed to it as if it were an expression. Iif() is the CF implementation of the Java ternary operator “?” which is exactly what it calls to do its work; this is faster than using <cfif><cfelse></cfif> to achieve the same effect. Once iif() has figured out which expression to evaluate, it simply delegates control to evaluate() to actually process the expression.

Here’s a simple example of a situation where you could use evaluate() and iif(). Not that you really need to in this case, but it should serve to illustrate usage.

<cfset temp = 70>
<cfset unit = "farenheit">

<cfset ftocConverter = "(5/9)*(temp-32)">
<cfset ctofConverter = "(9/5)*temp+32">

<cfif unit eq "celsius">
    <cfset clunkyConversion = evaluate(ctofConverter)>
<cfelse>
    <cfset clunkyConversion = evaluate(ftocConverter)>
</cfif>

<cfset slickConversion = iif((unit eq "celsius"), ctofConverter, ftocConverter)>

I hope you get the general idea. Cool stuff - you can plug in your own formulae at runtime. Apart from the fact that iif() can be faster, its syntax is also more compact.

Under the covers, the expression is compiled to a Java class and cached in a manner similar to the template cache, which I’ve described earlier. This cache is keyed using the string of the expression being evaluated, so if you try to evaluate the same expression again from anywhere in your code, you will not pay the cost of compilation - the Java class which has already been compiled for the expression will be reused.

So in cases such as Bert’s, where the expressions to be evaluated remain static, evaluate() and iif() will perform efficiently, simply because they’re using the same compiled class over and over again. A possible solution to Bert’s problem would be to store the generated CFML expressions in a configuration file, read them into a Struct in memory (perhaps on the application scope), and then use the evaluate() function on them to process the expressions at runtime.

However, if your code needs to dynamically create new expressions for every call, possibly with different sets of operations and variables, then evaluate() and iif() will perform poorly, since they will then have to perform a compile for each and every execution. To make matters worse, these dynamically constructed expressions could sometimes push static expressions out of the cache, negating any benefits that the cache might provide for static expressions.

This is not to say that you shouldn’t use dynamically constructed expressions - if you know that your code will create a limited set of dynamic expressions, evaluate() and iif() will still perform well. The problem will only occur when you have dynamic expressions that are unique on each and every execution. On the other hand this might be exactly the reason why you chose to use evaluate() or iif(), in which case you should be aware of the performance costs.

In summary - use evaluate() and iif() in situations where the expression to be evaluated remains static over time. Using them for dynamic expressions will prove inefficient, and may well negate any benefits that static expressions enjoy.

Update: Ben Nadel has done some performance benchmarking, and finds that iif() is just as fast as, if not faster than, <cfif><cfelse></cfif>.