Jul 18 2008
ColdFusion, General, Java, Performance
The past couple of days I've been messing around with a couple functions, cleaning them up a bit to blog about them. One of them is for color-coding SQL and the other for highlighting differences in two strings. Both are pretty small, but very repetitive in what they do. Depending on the size of the text you are processing, performance varied. Sometimes the code inside was repeated hundreds of thousands of times given a large enough test.I originally wrote the code on ColdFusion 7 and had been pleased with its behavior but the speed always left a little lacking. The first time I ran both of the functions on ColdFusion 8 I was absolutely floored. The same code ran dozens of times faster on a SLOWER server. The only difference-ColdFusion 8. That really is a testament to Adobe and the performance tuning they did in Scorpio. However, I wasn't totally satisfied with that. I also wanted to clean up performance on ColdFusion 7 as well. This code is definitely atypical. It does not represent the way 99% of your application works. Generally it makes no difference if one ColdFusion function takes 20 ms longer than another. When you only call the dang thing twice you don't notice. Everyone's knee-jerk reaction to performance testing is to make up a cfloop that iterates a few hundred thousand times and time the code. Generally that sort of test has absolutely no resemblance to a real life app. In this particular case though, it would actually be a bit more applicable. I didn't need to write any loops, the code already looped enough. I just needed to create a big enough test to keep the server busy for about 30 seconds or so while I looked at what it was up to. I use SeeFusion from WebApper to monitor my servers and I swear by it-- it is a very useful program. The slower lines of code are accentuated when you are running them a million times. It's kind of like when a drywall finisher turns off the lights and uses one bright spot light to illuminate all the small imperfections for the final sanding. I ran a stack trace several times over while running the code and immediately lines of code became clear bottlenecks. These finds are probably specific to my application, and I'm not suggesting to make any of these changes yourself, but for the record this is what I changed:
StringBufferRight off the bat, I saw a whole bunch of String.concat() going on. By default ColdFusion strings are stored as primitive Java strings which are immutable, meaning they cannot be changed after creation. Take the following code:
[code]<cfscript> foo = "Test1"; foo = foo & "Test2"; foo = foo & "Test3"; foo = foo & "Test4"; </cfscript>[/code]Each time I append test to foo, Java throws away the previous String and creates a new one which is the concatenated version. This is harmless in small amounts, but consumes tons of memory and CPU when done a few million times. If you can get away with it, the solution is to use <cfsavecontent>. This tag creates a buffer in memory and increases its size as needed. If cfsavecontent just won't work then our answer lies in Java! This code creates an instance of a StringBuffer object with an initial size of 50. (optional) As text is appended the String Buffer grows to accommodate.
[code]foo = createObject("java","java.lang.StringBuffer").init(javacast("int",50)); foo.append("Test1"); foo.append("Test2"); foo.append("Test3"); foo.append("Test4"); [/code]Go get your test back out, simply call the toString() method of your StringBuffer object. This brought the largest single gain cutting the execution time by more than half!
Compare instead of eqMy string compare function used the eq operator quite a lot and my stack traces showed a LOT of parseDouble() and NumberFormatException's being thrown. A nasty little habit of CF7 is to test a string to see if it is numeric. If it is, it will treat it as such (which I assume performs better?) if not, an error is thrown by the parseDouble method and caught internally. Then the string is treated as, well, a string. All this casting and error handling was being very costly and I knew my strings would rarely be numbers anyway. I switched over to the compare function which assumes you are using Strings and got another sizable increase in overall speed.
[code]<cfif string1 eq string2> <cfif compare(string1,string2) eq 0>[/code]