View comments | RSS feed

ColdFusion MX Coding Guidelines - Appendix: Performance Techniques

Release 3.2 (4/14/2005)

« Globalization | Contents | Revision History »

Printable Version

Appendix: Performance Techniques

Do not optimize unless you know you have a performance problem! In general, readability is more important than performance.

The biggest performance optimizations come from architectural and algorithmic changes, e.g., caching. Poorly written database queries can kill a server - use a query analyzer to sanity check your SQL and take advantage of cfquery's caching functionality where appropriate.

Performance under load is often very different to 'straight-line' performance - a change that makes a loop run twice as fast when you're testing a single request may not have the same effect when a hundred users are hitting your site. Use load testing tools to identify bottlenecks and then think carefully about how to restructure the code to improve performance under load.

Having said all that, here are some code-level "do's" and "don'ts". These techniques are usually version-specific and most of these have been verified on CFMX. For the most part they are only important for very performance-sensitive code such as frequently called tags.

Performance "Do's"

The following are 'positive' recommendations, e.g., "Do xyz..." or "Do xyz instead of...".

Use compareNoCase() for comparing two strings

Use compareNoCase() or compare() instead of the is not operator to compare two items. They are significantly faster. Remember that these functions return 0 if the values match, so they correspond to is not.

Example: <cfif compareNoCase(x, "a") neq 0>
Not: <cfif x is not "a">

Note: This has been verified for CFMX.

Use listFindNoCase() for OR comparisons

Use listFindNoCase() or listFind() instead of the is and or operators to compare one item to multiple items. They are much much faster (order of magnitude for 5+ options).

Example: <cfif listFindNoCase("a,b,c", x) is not 0>
Not: <cfif x is "a" or x is "b" or x is "c">

Note: This has been verified for CFMX.

Use arrays instead of lists - in general

In CFMX, lists suffer from the generally slow string processing in Java which means that list manipulation can be slower than in CF5. In general, it is better to work with arrays of items instead of lists of items: listGetAt() is not an efficient way to work with individual items in a data set! However, see the list vs array caveat in the Don't section below.

Note: This has been verified for CFMX.

Use cfqueryparam to increase query performance

You can use cfqueryparam to optimize a query that looks something like this:

SELECT
    *
FROM
    TABLE_NAME
WHERE
    COLUMN = #variable#

If this query is executed repeatedly with different values for variable then using a SQL 'bind' variable will be faster. cfqueryparam creates these 'bind' variables:

SELECT
    *
FROM
    TABLE_NAME
WHERE
    COLUMN = <cfqueryparam cfsqltype="cf_sql_xxx" value="#variable#">

This allows the optimizer to compile the query once and reuse it every time the query is executed. It is also more secure since it prevents rogue SQL from being passed into a query (because it validates the type of the data).

Note: This has been verified for CFMX.

Use blockFactor to increase query performance

Adding blockFactor to a query can significantly improve performance. To add blockFactor, examine the data that is being returned. Determine the maximum size (in bytes) of each row. Take that size and determine how many times that number would divide into 32k. That number is your blockFactor, but be aware that the max blockFactor is 100. So, if for example you were getting 200 bytes per row, you could easily fit over 100 rows into the 32k buffer that CF 'grabs' at one time.

If you know at runtime that you will have less then 100 rows returned, for example you're writing a query that either returns 0 or 1 rows, do not bother adding the blockFactor attribute.

Note: This has been verified for CFMX.

Performance "Don'ts"

The following are 'negative' recommendations, e.g., "Don't do xyz...".

Don't use evaluate()

Avoid evaluate() unless there is no other way to write your code (and there is almost always another way to write your code).

Don't use iif()

Always use cfif/cfelse instead of iif(). It is significantly faster and more readable.

Don't use structFind()

Always use struct.key or struct[key] instead of structFind(struct, key). They are significantly faster and more readable.

Don't slavishly convert lists to arrays

Even though manipulating an array is generally faster than manipulating a list in CFMX, if you simply need to iterate over a list of items and process each one in turn the faster construct is <cfloop list="#itemList#" index="x"> ... </cfloop>. Don't convert itemList to an array and then loop over that - it's not worth it because it probably won't be faster.

Don't use cfmodule

It's slower than a CFC method invocation, it's slower and uglier than using a custom tag with a prefix, it's even slightly slower than a regular custom tag invocation. Better options exist: use a CFC (preferred), use cfimport and invoke a custom tag using a prefix (always preferable to invoking a custom tag via cfmodule), or even simply include a file.

Don't use incrementValue()

Always use x = x + 1 instead of x = incrementValue(x). It is more readable and slightly faster.

Note: In some situations where x + 1 is not legal, incrementValue(x) may be more readable than creating a temporary variable to hold x + 1 and then using the temporary variable.

Don't use WDDX for hardcoded data

It is always faster to cfinclude a CFML file that defines a datastructure than it is to deserialize a WDDX packet of that datastructure (somewhat faster if the packet is in memory, and significantly faster if the WDDX packet is read with cffile). Use this technique if the datastructure can be hardcoded (i.e., don't ship .wddx files, ship .cfm files). The Site-wide Variables technique is a good example of this.

Note: Complex or frequently changing configuration data is best implemented using an appropriately designed XML file.

« Globalization | Contents | Revision History »

Comments


McGarnagle said on May 4, 2004 at 7:00 AM :
I've created a custom tag that uses WDDX for hardcoded data. It reads a wddx-serialized struct that contains english and french text elements for a page. I've not seen any major performance hits... on average, the processing time takes between 15 and 30 ms. How much of a reduction could I expect if I switched to the cfinclude approach?
SeanCorfield said on May 8, 2004 at 2:45 PM :
On low-traffic sites you probably won't notice much of a performance difference and, of course, a lot depends on whether you read the WDDX once and cache the data or whether you read it on each request.

As for embedding CF code in the 'labels', you'll still need the evaluate(DE()) trick with the include file. Don't forget that if your label has " or ' in it you'll need to escape it (double the quote):
&lt;cfset myLabel = "This is a ""string"" in a label"/&gt;
Amalaan said on Dec 1, 2004 at 12:50 PM :
Sorry, should have added "where relevant" to my post above. I'm aware that CF Script is only a subset of CFML.
Amalaan said on Dec 1, 2004 at 12:17 PM :
Hi,
Do the above guidelines also apply to CF Script? Can we assume that whatever advice is given re CFML also applies to CF Script?
SeanCorfield said on Dec 3, 2004 at 3:18 PM :
Yes, I guess these recommendations apply to cfscript as well but frankly I wouldn't worry too much about low-level optimization like this. Read the introductory section at the top of this page about algorithmic improvements and caching.

For example, I use 'is not' rather than 'compareNoCase()' because 'is not' is much easier to read - and readability is far more important than very minor improvements in performance.
jladams97 said on Dec 2, 2005 at 11:15 AM :
Unless you are absolutely CERTAIN that the strings you are comparing will never contain leading zeros, you should ALWAYS do string comparisons using Compare() and/or CompareNoCase() not because it is faster than using IS but because it is the only way to truly compare such strings as strings. If you use IS (or EQ) to compare "001" and "0001" you'll be told that the "strings" are the same. I say "strings" because CF is obviously not comparing strings there (else you wouldn't be told those two different strings are the same)--it is obviously comparing numbers. But Compare() and CompareNoCase() always compare actual strings.
SeanCorfield said on Dec 8, 2005 at 11:45 AM :
That's a very good point Joshua! Yes, that's a case where correctness outweighs the other concerns being discussed here and it is not immediately obvious that using "is" would cause that problem.

 

RSS feed | Send me an e-mail when comments are added to this page | Comment Report

Current page: http://livedocs.adobe.com/wtg/public/coding_standards/performance.html