Categories
Contact Doug!
Learn About Doug!
View Doug Boude's online resume
updated 11/18/2009

View Doug Boude's profile on LinkedIn
Link to me!

Follow Doug Boude on Twitter
Follow me!

Be Doug's friend on Facebook
Befriend me!
(I promise not to follow you home)
OO Lexicon
Chat with Doug!
Recent Entries
You may also be interested in...
Web Hosting

<< May, 2013 >>
SMTWTFS
1234
567891011
12131415161718
19202122232425
262728293031
Search Blog

Recent Comments
Re: Disappearing IE Popup Window During Save/Open Dialog (by LZ at 4/20 7:58 AM)
Re: Create Dynamic WHERE Clauses in PHP (by pooja at 3/20 7:29 AM)
Re: Just What IS a 'Service Layer', Anyway? (by EugenK at 3/07 7:56 PM)
Re: Using Google as your CF Mail Server (by 5starwebteam.com at 2/25 1:27 AM)
Re: Why Provide for Service layer objects in CFWheels? (by Steven Benjamin at 1/25 11:43 AM)
Re: What is an 'Advanced' Coldfusion Developer? (by ColdFusion Developer at 12/24 5:14 AM)
Re: Equivalent of SQL "TOP X" in Oracle (by Ashenafi Desalegn at 12/06 5:29 AM)
Re: PHP Export to Excel Snippet (by serene at 12/05 1:44 AM)
Re: Just What Is 'Application Logic', Anyway? (by Arif at 11/13 8:06 AM)
Re: Hosts File Changes Not Acknowledged on Vista 64 (by Aaron at 10/22 2:31 PM)
Re: PHP Export to Excel Snippet (by Jafar Shah at 10/10 4:28 AM)
Re: Viewing Option Text (in IE7) that's Wider than the Select List (by Chenelle S at 10/04 12:53 PM)
Re: PHP Export to Excel Snippet (by Kilo at 9/26 5:20 PM)
Re: Porting Coldfusion Code to Mura (by tariq at 9/03 9:51 AM)
Re: Just What IS a 'Service Layer', Anyway? (by James at 8/27 4:06 PM)
Re: Calculating Business Hours (by helen at 8/14 2:54 AM)
Re: What IS 'Business Logic', Anyway? (by dougboude at 8/06 11:30 AM)
Re: What IS 'Business Logic', Anyway? (by Adrianne at 8/06 10:29 AM)
Re: Family Law: The Weapon of Choice for Woman Scorned (by dougboude at 8/04 4:39 PM)
Re: Family Law: The Weapon of Choice for Woman Scorned (by Lola LB at 8/04 7:43 AM)
Archives
Photo Albums
Funnies (5)
Family (3)
RSS

Powered by
BlogCFM v1.11

15 April 2011
Approaches to Building Strings: The Imploding Array
stringBuffer vs Imploding Array

So I'm working on a method that needs to generate a potentially giant SQL script, so of COURSE I'm NOT going to use ColdFusion's string concatenation functionality...far too slow. In other words, I will NOT be doing this:

<cfset thestring = "" />
<cfloop query="qryData">
 <cfset thestring = thestring & "," & theField />
</cfloop>

So, the next obvious choice is to leverage the amazingly fast java stringBuffer object! I've used it before, and I know for a fact that it is an extremely high speed approach to building strings. However, there is another approach I use in PHP (since I don't have easy access to Java, that I am aware of), that also works very fast. So, I thought I'd implement it using ColdFusion as well and make it run a foot race with stringBuffer to see which was actually faster. Before I go on, let me demonstrate this other method I'm talking about.

<cfset aString = arraynew(1) />
<cfloop query="qryData">
 <cfset arrayAppend(aString,"check this value out: " & theField) />
</cfloop>
<cfset finalString = arrayToList(aString,"<br>") />

In a nutshell, for each line in my final string, I append an array item. When the array is fully populated, I simply turn it back into a list, using a line break as my delimiter. In PHP I would "implode" the array, but arrayToList accomplishes the same thing.

Here is the code we'll be using to do the same thing with stringBuffer:

<cfset csvstr = createObject("java","java.lang.StringBuffer")>
<cfloop query="qryData">
 <cfset csvstr.append("check this value out: " & theField & "<br>") />
</cfloop>
<cfset finalString = csvstr.toString() />

Here are the results of running 40 tests over a 50,000 record query:

I think the moral of this story is obvious ;)

Here is the actual code I used to conduct the tests:

<cfparam name="application.counter" default=0 />
<cfparam name="application.jtotal" default=0 />
<cfparam name="application.atotal" default=0 />
<cfparam name="application.jAverage" default=0 />
<cfparam name="application.aAverage" default=0 />
<cfparam name="reset" default=false />

<cfif reset>
 <cfset application.counter = 0 />
 <cfset application.jtotal = 0 />
 <cfset application.atotal = 0 />
 <cfset application.jAverage = 0 />
 <cfset application.aAverage = 0 />
</cfif>

<!--- increment my counter --->
<cfset application.counter = application.counter + 1 />

<!--- execute query to retrieve 50K records --->
<cfquery name="qryData" datasource="glock">
 select invoicenum,mid(comment,1,50) as comment from ilog_text LIMIT 0,50000
</cfquery>

 


<cfoutput>

<!--- LET'S DO THE STRINGBUFFER METHOD --->
<!--- create stringbuffer object --->
<cfset csvstr = createObject("java","java.lang.StringBuffer")>
<!--- add initial header line --->
<cfset csvstr.append("invoicenum,comment" & "<br>" )>
<!--- start our counter... --->
<cfset jstart = getTickCount() />
<!--- loop over the query results and build the string --->
<cfloop query="qryData">
 <cfset csvstr.append(invoicenum & "," & comment & "<br>")>
</cfloop>
<!--- generate the final string... --->
<cfset finalstring = csvstr.toString() />
<!--- stop the clock --->
<cfset jend = getTickCount() />
<!--- capture stringBuffer stats --->
<cfset thisJTime = jend-jstart />
<cfset application.jtotal = application.jtotal + thisJTime />
<cfset application.jAverage = application.jtotal/application.counter />

<!--- LET'S DO TH ARRAY METHOD... --->
<!--- create our string array --->
<cfset aLines = arraynew(1) />
<!--- add header line --->
<cfset arrayappend(aLines,"invoicenum,comment") />
<!--- start our counter --->
<cfset astart = getTickCount() />
<!--- loop over the query results and build our array of strings --->
<cfloop query="qryData">
 <cfset arrayappend(aLines,invoicenum & "," & comment)>
</cfloop>
<cfset delim = "<br>" />
<!--- generate the final string... --->
<cfset finalstring = arrayToList(aLines,delim) />
<!--- stop the clock --->
<cfset aend = getTickCount() />
<!--- capture and output array method stats --->
<cfset thisATime = aend-astart />
<cfset application.atotal = application.atotal + thisATime />
<cfset application.aAverage = application.atotal/application.counter />

<!--- OUTPUT THE RESULTS TO THE SCREEN --->
Records being parsed: #qryData.recordcount# &nbsp;&nbsp;&nbsp;&nbsp;Number of executions: #application.counter#<hr>
(time in milliseconds)<hr>
THIS JAVA TIME: #thisJTime#&nbsp;&nbsp;&nbsp;&nbsp;AVERAGE JAVA TIME: #application.jAverage#<hr>
THIS ARRAY TIME: #thisATime#&nbsp;&nbsp;&nbsp;&nbsp;AVERAGE ARRAY TIME: #application.aAverage#<hr>
Array method is #decimalformat(application.aAverage/(application.aAverage + application.jAverage)) * 100#% faster!
</cfoutput>




Posted by dougboude at 1:11 PM | PRINT THIS POST! |Link | 7 comments
Subscription Options

You are not logged in, so your subscription status for this entry is unknown. You can login or register here.

Re: Approaches to Building Strings: The Imploding Array
you should try cfsavecontent
Posted by Henry on April 15, 2011 at 2:11 PM

Re: Approaches to Building Strings: The Imploding Array
I've used the array method almost exclusively building these types of strings for CSVs. I had found the exact same savings you did. @Henry, cfsavecontent causes too much whitespace problems and isn't faster at building the string.
Posted by Phil Duba on April 15, 2011 at 2:17 PM

Re: Approaches to Building Strings: The Imploding Array
One thing you may also want to test for this is to see how fast a cfSaveContent would work. I've found in previous tests it has been much faster than using StringBuffer or the CF Array method.

Anyhow here's my modification of the test, hopefully the cf code will come through your filter:


<!--- CFSAVECONTENT METHOD... --->
<!--- start counter --->
<cfset astart = getTickCount() />
<!--- loop over the query results and build the final variable --->
<cfsavecontent variable="finalstring">
invoicenum,comment<br />
<cfoutput query="qryData">#qryData.invoicenum#, #comment#<br/></cfoutput>
</cfsavecontent>
<!--- stop the clock --->
<cfset aend = getTickCount() />
<!--- capture and output array method stats --->
<cfset thisATime = aend-astart />
<cfset application.atotal = application.atotal + thisATime />
<cfset application.aAverage = application.atotal/application.counter />
Posted by larryclyons on April 15, 2011 at 2:36 PM

Re: Approaches to Building Strings: The Imploding Array
Actually, SaveContent is faster in the tests I've just done.

cfsavecontent = 37.1ms average
ArrayAppend = 45.9ms average

The reason I tested it is because I wanted to do another test - is there a difference between:

StringBuffer.append(qryData.invoicenum & "," & qryData.comment & "
")
vs
StringBuffer.append("#qryData.invoicenum#,#qryData.comment#
")

Well, there is, but only ~5ms (67.9ms -> 63.3ms) on average.

Here's the code I used for all of this: https://gist.github.com/922346
Posted by Peter Boughton on April 15, 2011 at 2:54 PM

Re: Approaches to Building Strings: The Imploding Array
Though of course, when you're talking about times in the region of 40-50ms, for 50k records, worrying about which method is fastest is a little pointless.

Unless this is something *really* time critical, go with whichever method you find easiest to maintain.
Posted by Peter Boughton on April 15, 2011 at 3:00 PM

Re: Approaches to Building Strings: The Imploding Array
huh, i never even considered using cfsavecontent for this! Awesome to know there's yet another option as well! :)
Posted by dougboude on April 15, 2011 at 3:09 PM

Re: Approaches to Building Strings: The Imploding Array
Quick correction on my comment above, on the offchance that anyone cares... the & vs # method does NOT have a 5ms difference. They are actually as close as makes no difference.

The 5ms in my test is because I accidentally used the (non-threadsafe) StringBuilder class, instead of StringBuffer.
Posted by Peter Boughton on April 15, 2011 at 3:29 PM

Name:   Required
Email:   Required your email address will not be publicly displayed.

Want to receive notifications when new comments are added? Login/Register for an account.

Time to take the Turing Test!!!

Five plus Eleven equals
Type in the answer to the question you see above:

Your comment:

Sorry, no HTML allowed!