所以,正如我的标题所说,我有一个 ColdFusion 程序,过去需要10分钟才能在我们的服务器上运行,但现在运行在< 15 秒。很困惑为什么这么简单的程序需要10分钟我和我的老板检查它以找出导致速度减慢的culprit
部分代码。我们最终将从10分钟变为 5-10秒来运行。
现在我们不确定为什么修复是一个修复,所以我们想知道是否有人可以向我们解释为什么/如何工作,以便我们可以理解修复,以便我们可以利用加速在其他计划中。
这个程序的开头是一个抓取 ~4800条记录的查询(没有任何离谱),然后循环浏览这些记录,我们发现这是慢速部分。这是我们所拥有的一个粗略的例子,以及我们为解决它所做的工作。 TextString 在查询附近的顶部设置为我们返回的字段的标题。
旧代码:
<cfloop>
<CFSET TextString = TextString & DriverID & TabChar & LocalSSN & TabChar & FirstName & CarriageReturn & LineFeed>
</cfloop>
固定代码:
<cfloop>
<CFSET LocalTextString = "">
<CFSET LocalTextString = LocalTextString & DriverID & TabChar & LocalSSN & TabChar & FirstName & CarriageReturn & LineFeed>
<CFSET TextString = TextString & LocalTextString>
</cfloop>
答案 0 :(得分:21)
由于字符串在CF中连接的方式,您的代码几乎肯定更快。虽然我不知道CF的确切内部,但我怀疑Strings是不可改变的。这意味着每次使用&amp;将一个额外的变量连接到String时,它将创建一个新的String,包含旧的String和最后的新字符串。 要做到这一点,它必须分配内存,随着字符串的增长,内存越来越多。
每个循环中都有8个变量被添加到循环中,包括字符串的先前版本,所以你在循环期间分配~4800 * 8个字符串。
假设每行长度为35个字符,则字符串的最终大小为168k。 这意味着它在运行期间的平均尺寸是:84k。 现在,请注意你要分配4800 * 8的字符串,你使用多达3 GIG (4800 * 8 * 8400)的内存来创建168k的输出。这意味着Java必须完成一大堆垃圾收集和额外的工作来为您的代码提供服务。
你的更新代码正在使用LocalTextString 7次中的7次,相比之下它会很小,因此你可以获得相当大的速度提升。
试试这个版本:
<cfset buffer=ArrayNew()>
<cfset crlf=CarriageReturn & LineFeed />
<cfloop>
<cfset ArrayAppend(buffer,DriverID)>
<cfset ArrayAppend(buffer,TabChar)>
<cfset ArrayAppend(buffer,LocalSSN)>
<cfset ArrayAppend(buffer,TabChar)>
<cfset ArrayAppend(buffer,FirstName)>
<cfset ArrayAppend(buffer,crlf)>
</cfloop>
<cfoutput arrayToList(buffer, "")/>
它构建了一个字符串数组,然后在最后将它们转换为一个字符串。你也可以从Java看一下StringBuffer,它做同样的事情。当我上次查看它时,上面的Array / List方法速度最快,但那是CF的几个版本。
我对答案中的猜测数量感到不满意,所以我有机会重现这个问题。我使用了一台带有CF10的5岁Mac进行测试。
如果我在运行测试时将VisualVM挂钩到ColdFusion进程,我可以看到原始代码在运行期间咀嚼了几百兆内存。没有我最初的数学建议那么糟糕;我怀疑它只是每次循环创建一个大字符串,而不是每个单独连接一次。它会在每次运行期间触发2-3个小的垃圾收集,这会使性能下降。它还在运行的核心上使用100%的CPU。
ArrayAppend代码平均不会触发GC,您很难看到正在使用的内存。
我不明白为什么原始代码需要花费很长时间,但其他因素包括CPU速度,可用内存,访问数据库(我使用MySQL5),CF版本等。