During some development I ran into the issue of concatenating string which seems slow when doing it manually instead of a native function
<cfset start = gettickcount()>
<cfset dummyArray = ArrayNew(1) />
<cfset concatedString = "" />
<cfset concatenatedByFunctionString = "" />
<cfloop from="1" to="100000" index="i">
<cfset arrayAppend(dummyArray, RandRange(1000000, 9999999)) />
</cfloop>
<cfoutput>Prepping data: #(gettickcount() - start)/1000# seconds<br/></cfoutput>
<cfset start = gettickcount()>
<cfloop from="1" to="100000" index="j">
<cfset concatedString = concatedString & dummyArray[j] />
</cfloop>
<cfoutput>concatenating data in loop: #(gettickcount() - start)/1000# seconds<br/></cfoutput>
<cfset start = gettickcount()>
<cfset concatenatedByFunctionString = arraytoList(dummyArray,'')/>
<cfoutput>concatenating data in arraytoList function: #(gettickcount() - start)/1000# seconds<br/></cfoutput>
<cfif structKeyExists(url,'debug')>
in loop:<br/>
<cfdump var="#concatedString#"/>
in function:<br/>
<cfdump var="#concatenatedByFunctionString#"/>
</cfif>
This is not the issue however. When you run this script in Coldfusion the time taken to prep the data is around 150ms, in Railo this is over 3000ms. When replacing the ArrayAppend with a cfset the loop gets much faster.
Any idea on what I do wrong?
edit: after some additional testing we came with the following results:
script used:
<cfset start = gettickcount()>
<cfloop from="1" to="10000" index="i">
</cfloop>
<cfoutput>Empty loop 10 000: #(gettickcount() - start)/1000# seconds<br/></cfoutput>
<cfset start = gettickcount()>
<cfloop from="1" to="100000" index="i">
</cfloop>
<cfoutput>Empty loop 100 000: #(gettickcount() - start)/1000# seconds<br/></cfoutput>
<cfset start = gettickcount()>
<cfloop from="1" to="100000" index="i">
<cfset local.tmp = 1/>
</cfloop>
<cfoutput>loop 100 000 cfset: #(gettickcount() - start)/1000# seconds<br/></cfoutput>
<cfset start = gettickcount()>
<cfscript>
string = CreateObject("java", "java.lang.String");
array = CreateObject("java", "java.lang.reflect.Array");
dummyArray = array.newInstance(string.getClass(), 10000);
</cfscript>
<cfloop from="0" to="9999" index="i">
<cfset array.set(dummyArray, i, "00000") />
</cfloop>
<cfoutput>loop 10 000 java array set: #(gettickcount() - start)/1000# seconds<br/></cfoutput>
<cfset start = gettickcount()>
<cfset dummyArray = ArrayNew(1) />
<cfloop from="1" to="100000" index="i">
<cfset arrayAppend(dummyArray, '00000') />
</cfloop>
<cfoutput>loop 100 000 arrayAppend: #(gettickcount() - start)/1000# seconds<br/></cfoutput>
<cfset start = gettickcount()>
<cfset dummyArray = ArrayNew(1) />
<cfloop from="1" to="100000" index="i">
<cfset dummyArray[i] = '00000' />
</cfloop>
<cfoutput>loop 100 000 array set via brackets: #(gettickcount() - start)/1000# seconds<br/></cfoutput>
output:
Railo (4.2.1.008 final):
Empty loop 10 000: 0.001 seconds
Empty loop 100 000: 0.013 seconds
loop 100 000 cfset: 0.05 seconds
loop 10 000 java array set: 8.095 seconds
loop 100 000 arrayAppend: 1.96 seconds
loop 100 000 array set via brackets: 2.103 seconds
Adobe CF 10:
Empty loop 10 000: 0.002 seconds
Empty loop 100 000: 0.059 seconds
loop 100 000 cfset: 0.115 seconds
loop 10 000 java array set: 0.014 seconds
loop 100 000 arrayAppend: 0.139 seconds
loop 100 000 array set via brackets: 0.15 seconds
Any Insight / idea on this?
Thanks.
edit2:
my real world issue:
<cfinclude template="/queries/pr_web_shop_country_seo.cfm" >
<cfloop query="qry_web_shop_country_seo">
<cfif qry_web_shop_country_seo.generate_sitemap eq 1>
<cfset types_to_generate = ArrayNew(1) />
<cfset types_to_generate[1] = "PRODUCTS" />
<cfset indexList = ArrayNew(1) />
<cfset locale = qry_web_shop_country_seo.country_code />
<cfset id_language = qry_web_shop_country_seo.id_language_default />
<cfset file_suffix = "-" & replace(qry_web_shop_country_seo.url_domain,'.','-','ALL') & ((qry_web_shop_country_seo.url_suffix eq "")? "" : "-" & qry_web_shop_country_seo.url_suffix) />
<cfinclude template="/queries/pr_seo_urls.cfm">
<cfloop array="#types_to_generate#" index="type">
<cfswitch expression="#type#">
<cfcase value="PRODUCTS">
<cfset productData = ArrayNew(2)>
<cfset counter = 1>
<cfloop query="qry_seo_urls">
<cfif qry_seo_urls.source eq 'PRODUCT'>
<cfset arrayAppend(
productData[Ceiling(counter/max_link_list)]
,url_prefix & qry_web_shop_country_seo.url_domain & qry_web_shop_country_seo.root & qry_seo_urls.seo_url)
/>
<cfset counter = counter + 1 />
</cfif>
</cfloop>
</cfcase>
</cfswitch>
</cfloop>
</cfif>
</cfloop>
If you're looking for the fastest solution, then try using an java.util.ArrayList
. I tried it on Lucee 4.5 and it performed well so may suit your needs.
<cfset start = gettickcount()>
<cfset dummyArray = createObject("java", "java.util.ArrayList")>
<cfloop from="0" to="9999" index="i">
<cfset dummyArray.add("00000")>
</cfloop>
<cfoutput>
loop 10,000 java.util.ArrayList add: #(gettickcount() - start)/1000# seconds
</cfoutput>