In the past, some developers thought that ColdFusion was “too slow.” Regardless of whether that was true or not in the past, it is certainly not true now. With ColdFusion MX running on top of Java, CF has gotten a significant performance boost. However, aside from the tweaks to CFMX itself and the way it compiles code to Java, there are still things that can drag down a server. In this article, I’d like to show you some ways that you can further enhance the speed of your CF applications.
CFMX 6.1 ships with and runs under version 1.4.2 of Sun’s Java Virtual Machine (JVM). While this was a nice upgrade from the older JVM that CFMX 6.0 came with, it doesn’t mean that you can forget about this crucial aspect of the server. You should keep your eye on Sun’s Java Web site, and when a new version (like 1.5.0) is released, you should give serious consideration to updating the JVM that CFMX uses. In addition to adding new features, Sun also improves performance with each new JVM release. You can set the JVM, along with minimum and maximum memory heap sizes, in the “Java and JVM” section of the ColdFusion administrator.
Once you have CFMX and Java running in their latest forms, there are some server settings that can be adjusted to improve performance. On the Settings page of the ColdFusion administrator is a setting for Maximum Simultaneous Requests. Setting this value is more art than science, because it depends greatly on the specific server and application. Nonetheless, it is an important setting. If this is too low, you’ll be queuing up requests and slowing things down. If it’s too high, the CPU will bog down trying to process too many threads at once. A general rule that has worked for me is to set this to about 3-4 times the number of processors in the server. Again though, this is just an estimate. The only true way to get the best number for your server is to run some load tests with different settings.
Under Caching in the CF administrator, you’ll find several options that can affect performance. The purpose of each one is explained pretty clearly right on the page, but I’ll run through them for good measure. You’ll want a high number for Maximum Cached Templates, equal or greater than the number of templates running on the server. On a production server, where files don’t change much, you will want to check Trusted Cache. This way, CF will assume that the version of the template it has in memory is the latest version, and it won’t check against the version on disk. You’ll also want to check Save Class Files. CF will save the compiled class files that it generates from your CFML templates to the hard drive. CF will then use the saved class files instead of recompiling for each request.
A final performance boost can come from disabling debugging. I don’t mean just turning off all the debugging IP addresses; I mean actually turning off debugging completely. When running applications that use ColdFusion Components extensively, such as a Mach-II application, debugging can greatly affect the processing speed for that application. In my experiments, turning off debugging increased the performance of a fairly simple Mach-II application by six times!
One of the most notorious bottlenecks for a CF application (or any Web application for that matter) relates to databases. Virtually all Web applications interact with a database to some degree. This can be a spot where people just sort of assume that “the database will handle it.” And to some degree, this is true, because the latest versions of most enterprise-level RDBMSs have incredibly clever query optimizers built into them. Still, there are some things the database just can’t do on its own.
One could (and indeed many have) write an entire book on optimizing databases and SQL queries. My intent here is to point out a few places where, in my experience, a simple change made a significant impact on performance.
The first is proper indexing. When you query a database table and there is no index on the field(s) in your WHERE or JOIN clauses, the database has no choice but to do a full table scan to look for matches. If your table has millions of rows in it, this is a very bad thing. Indexing the appropriate fields can greatly increase the performance of your SELECT statements.
Primary key fields should already be indexed by the database, but keep an eye out for other fields used in WHERE or JOIN clauses that are not indexed, and consider indexing them if they are frequently used. However, don’t just go indexing every column in every table. As with everything in the computing world, indexes can have a downside. First, they take up disk space. And second, while they speed up SELECT statements, they slow down INSERT and UPDATE statements because the indexes must be rebuilt to reflect the new data.
Another nice boost in speed can come from the use of bind variables. In CF, this is accomplished by using the <cfqueryparam> tag in your SQL statements. Not only is this more secure than simply using a CF variable directly in your SQL, but it’s faster as well. This is because using a bind variable tells the database something like this: “Keep this SQL statement in memory, and know in advance that the only things that will be changing are the bind variables–everything else will stay the same.” In addition, you specify the variable type within <cfqueryparam>, so the database knows to always expect the same data type.
Finally, CF offers a great option for caching database queries completely into CF server memory. This completely bypasses calls to the databases and results in very fast performance. And just like most things in CF, it’s extremely easy to do. Simply add the cachedwithin attribute to your <cfquery> tag. For example, the example in Listing A will cache the query in memory for one hour.
The result set will be cached in CF server memory, and future requests for the same (the exact same) SQL statement will use the cached result set until the cache timeout is reached. One downside here is that you can’t use bind variables within queries that you cache using cachedwithin.
If you are feeling adventurous, consider looking into stored procedures, triggers, and user-defined functions if your RDBMS supports them. These can be complex to learn and implement but they deliver some compelling performance and security benefits.
CFML has a handy tag (<cfcache>) that lets you cache entire pages. You can set this tag to cache on the client, the server, or both. By specifying a timeout with the “timespan” attribute, you tell CF how long to keep the page cached. You can also specify the directory to which CF will write the cached page for subsequent reads. By default, this is the \cache directory off of your CFMX root.
While <cfcache> works in some situations, it has some big drawbacks. First, since it’s writing the cached files to disk, performance drops off as more cached pages are managed. And it’s difficult to cache just certain parts of a page with <cfcache>. With these issues in mind, Brandon Purcell created the <cf_accelerate> custom tag.
This tag is really amazing. You can wrap it around all or part of your page content and use it to cache that content. And the great thing is that it stores the cached data in the server memory (using the application scope) instead of on disk. The results are impressive. My simple tests showed that including cached and noncached files across 10,000 loop iterations, using <cf_accelerate> was five times faster. And Brandon has done more robust tests where he was able to get CFMX on his 2-GHz laptop delivering an astounding 310 pages per second. Full details on the use of the <cf_accelerate> tag are available.
CF just keeps getting faster, and Macromedia has done an excellent job of continuing to draw more performance out of it. But there are always things that you can do on the development and server administration side to give your applications an extra speed boost. I hope that you can find ways to use some of these tips and techniques to speed up your own CF apps.