Coding for Performance


Performance considerations should always be on the mind of any quality developer. ColdFusion application performance often is rooted in the application code itself. In instances where code is written improperly or when necessary code elements are not included at all, performance and functionality can suffer dramatically. The following sections discuss issues that are key to improving performance in your applications.

Locking of Server, Application, and Session Variables

A commonly missed item dealing with application performance is the locking of shared scope variables. These variables should be locked in any code where server, application, or session-scoped variables are either written to or read from. Appropriate locking of server, application, and session variables must be applied or you risk degrading application performance and crashing your server.

Unless you properly implement locking, memory corruption can and will occur, which would further cause the ColdFusion Server to have threads hang when simultaneous user load is applied. To stop the inevitable memory leak, you must use the CFLOCK statement to prevent memory collision.

For writing to a scoped variable, use the type="exclusive" attribute:

 <cflock scope="Application" type="exclusive" timeout="10">        <cfset Application.myAppVar = 1>             other Application variable statements …  </cflock> 

For reading a scoped variable, use the type="readonly" attribute:

 <cflock scope="Application" type="readonly" timeout="10">        <cfset anyVariable = Application.myAppVar >             other Application variable statements …  </cflock> 

You can group multiple variables if they are all reads or all writes:

 <cflock scope="Application" type="exclusive" timeout="10">        <cfset Application.myAppVarX = 1>        <cfset Application.myAppVarY = 2>        <cfset Application.myAppVarZ = 3>             other Application variable statements …  </cflock> 

Also, never mix scoped variable types within a CFLOCK tag. Thus, the following would not be appropriate:

 <cflock scope="Application" type="exclusive" timeout="10">        <cfset Session.mySessionVar = 1>        <cfset Application.myAppVar = 1>  </cflock> 

The following also would not be appropriate:

 <cflock scope="Application" type="exclusive" timeout="10">        <cfset anyVariable = Application.myAppVar>        <cfset Application.myAppVar = 1>  </cflock> 

For more information on how to properly lock access to memory variables, refer to Chapter 8, "Application Framework."

Assigning Values to Application Variables for Each Page Request

When using the Application.cfm file (which is loaded with each page request) to assign application-scoped variables, be certain to encapsulate the variable assignments within a CFIF statement. Without doing this, the variables would be assigned for every page in the site, and thus would not be used properly. Simply test if an application-scoped variable exists. If it doesn't exist, assign all application-scoped variables. If it does exist, the assignment code is bypassed.

When setting application variables in the Application.cfm file, wrap the statement(s) within a CFIF statement:

 <cflock type="Application" type="exclusive" timeout="10">    <cfif NOT IsDefined("Application.ApplicationName">      <cfset application.applicationvar1="some value">      <cfset application.applicationvar2="another value">    </cfif>  </cflock> 

It is also important to note that this same technique should be used if session scoped variables are being assigned within the Application.cfm template.

Overuse of CFOUTPUT

ColdFusion developers have heard for years that ColdFusion parses any code that lies within a CFOUTPUT tag. This has all too often led developers to write code which grossly overuses the CFOUTPUT tag and causes ColdFusion Server to work harder.

Developers should make it a goal to write clean and readable code, of course, and in regards to the CFOUTPUT tag, developers should have only as many of them on a single page as absolutely necessary. The following example shows overuse of the CFOUTPUT tag:

 <input type="text" value="<cfoutput>#myValue1#</cfoutput>"...  <input type="text" value="<cfoutput>#myValue2#</cfoutput>"...  <input type="text" value="<cfoutput>#myValue3#</cfoutput>"... 

Instead of the preceding code, you should wrap a single CFOUTPUT tag around a block of code that contains several variables to be evaluated:

 <cfoutput>       <input type="text" value="#myValue1#">       <input type="text" value="#myValue2#">       <input type="text" value="#myValue3#">  </cfoutput> 

This code executes faster than the previous example and is easier to read and maintain.

Avoid Using Subqueries

It is a good practice to avoid using subqueries in your SELECT statements. The reason is that this causes the database engine to create an execution plan that is not or might not be the most efficient. Because the database engine does not know what the final values of the subquery might be, it must plan to look through the first table or set of tables as many times as the number of records that could be returned by the subquery.

This is an example of using a subselect:

 <cfquery name="getEmployees" datasource="ExampleApps">    SELECT FirstName         , LastName         , Email      FROM tblEmployees     WHERE DeptIDFK IN (SELECT DepartmentID                          FROM tblDepartments                         WHERE DepartmentName = 'Web Development')  </cfquery> 

This is an example of using multiple queries:

 <cfquery name="getDepartment" datasource="ExampleApps">    SELECT DepartmentID      FROM tblDepartments     WHERE DepartmentName = 'Web Development'  </cfquery>  <cfquery name="getEmployees2" datasource="ExampleApps">    SELECT FirstName         , LastName         , Email      FROM tblEmployees     WHERE DeptIDFK IN (#ListQualify(ValueList(getDepartment.DepartmentID), "'")#)  </cfquery>  

Use CFPARAM to Set Default Values

Developers often use IF statements within their applications to test for the existence of a variable and to set default values if that variable does not exist. This can be streamlined by using the CFPARAM tag to assign default values to a variable. The following example illustrates this point:

 <cfif NOT IsDefined("myVarName")>        <cfset myVarname=1>  </cfif> 

The same logic can be replicated as follows:

 <cfparam name="myVarName" default=1> 

Use CFSWITCH Instead of Multiple CFIF Statements

Developers should never use multiple IF statements to test for different values of the same variable. If there are more than two possible values for a single variable, you should use the CFSWITCH/CFCASE statement to enhance performance:

 <cfif lastname = "Ross">        <cfset myOtherVar="foo">  </cfif>  <cfif lastname = "Cummings">        <cfset myOtherVar="bar">  </cfif>  <cfif lastname = "Sen">        <cfset myOtherVar="foobar">  </cfif> 

This logic works, but is not very efficient. If you use this type of code, each and every CFIF statement is evaluated for every pass through the page unnecessarily. In other words, if the first CFIF statement is true, the other CFIF statements really don't need to be evaluated the condition has been met. Even though the first condition might be true, all the following CFIF statements are evaluated unnecessarily. To enhance performance, change this logic to use the CFSWITCH statement instead:

 <cfswitch expression="#lastname#">  <cfcase value="Ross">    <cfset myOtherVar="foo">  </cfcase>  <cfcase value="Cummings">    <cfset myOtherVar="bar">  </cfcase>  <cfcase value="Sen">    <cfset myOtherVar="foobar">  </cfcase>  </cfswitch> 

With this logic, only the statement that evaluates to true is executed, and the other conditions are bypassed. This takes the load off the processor by not requiring it to evaluate multiple CFIF statements.

Two Function Calls to a Database to Retrieve the Unique ID of Inserted Records

Many developers commonly use two query calls to retrieve the value of a newly created record. The first query executes the insert of the new row into the table. The second query uses the MAX() function of the structured query language (SQL) to retrieve the record with the highest unique ID value.

 <cfquery name="InsertNames" datasource="#request.dsn#">     INSERT INTO Authors (firstname, lastname, email)     VALUES ('#form.fname#', '#form.lname#', '#form.email#')  </cfquery> 

This request does not return any values so it requires a second query. The next request retrieves the MAX identity field, which might or might not come from the previous INSERT query.

 <cfquery name="getNewRecord" datasource="#request.dsn#">       SELECT FROM Authors            MAX(AuthorID)  </cfquery> 

Although this method does work under a minimal load, it is possible that errors can occur when the site is put under heavier load. If multiple users attempt to run the page at the same time, there is a chance that one of the values might become orphaned, which can result in database errors within the application.

It is better to retrieve the unique ID in a single query. By doing this, you eliminate the loss of data and improve application performance. You also alleviate the need to make multiple requests to the database.

Although this approach does work, the same functionality can be achieved with the following:

 <cfquery name="InsertNames" datasource="#request.dsn#">     INSERT INTO Authors (firstname, lastname, email)     VALUES ('#form.fname#', '#form.lname#', '#form.email#')        SELECT AuthorID  </cfquery> 

Now the single request to the database inserts the record and returns the value of the identity field of the newly created record. This method ensures that no records are orphaned no matter how many users execute the page simultaneously. In addition, you get a performance enhancement by making only one request to the database.

Do Not Overuse the # Symbol

The # symbol is for outputting the values of ColdFusion variables to the browser. It is not needed within a ColdFusion tag such as CFSET or CFIF. You need to wrap the variable name with the # symbols only when the variable is used within an area where quotation marks are used. This concept is best explained within the following code snippet:

 <cfif UserID EQ 33>    <!--- ColdFusion Statements --->  </cfif> 

Now assume you need to use the variable as part of another variable assignment:

 <cfif UserID EQ 33>        <cfset formAction = "updateRecord.cfm?uID=#userID#">  </cfif> 

This time, the # symbols had to be in place to correctly assign the value of the formAction variable because the variable (UserID) is inside the quotation marks. Had the variable been outside the quotation marks, the # symbols would not have been used:

 <cfif UserID EQ 33>        <cfset formAction = "updateRecord.cfm?udocText">Both of the two previous examples give the variable formAction a value of "updateRecord.cfm?uID=33". If the # symbol was not used and the variable was inside the quotation marks, as in the following code:

 <cfif userID = 33>        <cfset formAction = "updateRecord.cfm?uID=userID">  </cfif> 

the variable formAction would contain the value "updateRecord.cfm?uID=userID".

Replace() Function Instead of CFLOOP

Some developers use a CFLOOP statement to loop through a list and concatenate a variable to create the WHERE clause of a SQL statement. Here is an example of the CFLOOP logic to create the clause:

 <cfset tempVar="">  <cfloop index="listElement" list="aList">        <cfset tempVar = tempVar & listElement & " or ">  </cfloop> 

This logic can easily be replicated and performance can be increased by using the Replace() function instead. An example of the Replace() function is shown in the following code:

 <cfset tempVar = Replace(aList, ",", " or ", ALL)> 

This one simple line of code performs the exact same functionality, but results in a large performance increase.

Use Function Calls Instead of Comparisons in CFIF Statements

Within CFIF statements, it makes sense to use ColdFusion functions to evaluate values rather than operators to determine the comparisons. If you put this practice into use, it results in increased application performance. Use something like the following:

 <CFIF NOT LEN(TRIM(myFoo))> 

rather than:

 <CFIF myFoo IS NOT " "> 

Reduce Whitespace with CFSETTING, CFSILENT, and CFPROCESSINGDIRECTIVE

Formatted whitespace can add weight to a ColdFusion template. To reduce whitespace that is created during the creation of variables in ColdFusion, you should call the CFSETTING with the attribute "enableCFoutputOnly" at the beginning and end of both the Application.cfm and OnRequestEnd.cfm templates. This reduces the formatted whitespace and decreases your overall file size. The result is improved processing time and higher bandwidth utilization.

In addition, you can use the CFSILENT tag to wrap code that does not require output to be sent to the browser. This keeps extraneous whitespace from being generated in the output stream by that code. A good example of CFSILENT's usage would be to wrap query statements at the beginning of a .CFM template.

Lastly, you can use the CFPROCESSINGDIRECTIVE with the attribute "SuppressWhitespace" to further eliminate the extraneous whitespace that is generated through execution of ColdFusion templates.

Request Scope

The request scope was introduced in ColdFusion 4.5. It functions pretty much just like a local variable, but can be seen within custom tags. You should make use of the request scope wherever possible in place of shared scope variables to reduce the need for locking throughout your application.

Use Functions to Resolve Criteria

It is recommended that you use function calls to resolve criteria of conditional logic. It improves performance in your application. The following is a very simple example of this concept:

 <cfif NOT Len(foo)>    <!--- ColdFusion Statements --->  </cfif> 

The preceding code yields better performance than the following:

 <cfif foo eq ""> 


Inside ColdFusion MX
Inside Coldfusion MX
ISBN: 0735713049
EAN: 2147483647
Year: 2005
Pages: 579

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net