为什么巨大的字符串需要花费大量的时间进行初始化?

时间:2016-12-06 13:09:44

标签: string vba performance

我有一个非常技术性的问题,我无法想象答案,我想得到优化问题的建议。

在我的工作表中,我构建了很多XML行。具体来说,它们分布在整个范围内的389,256行(" A1") - >范围(" A389256&#34)。

我的目标是构建一个包含所有这些行的字符串,从而将其填充到XML文件中。我使用以下代码执行此操作:

Private Function buildFileText() As String

    Dim ss As String
    Dim j As Long

    For j = 1 To Sheets("FileContent").Range("A1").End(xlDown).Row
        ss = ss & Sheets("FileContent").Range("A" & j).Value & vbNewLine
    Next j

    buildFileText = ss

End Function

基本上,我只是从空字符串开始构建字符串,并逐行添加电子表格的所有内容。

什么是惊吓是这段代码执行的时间:我在For循环之前和之后放置了一个计时器,它花了 1小时44分钟执行。

我没有发现这种行为是正常的,因为虽然行数很多,但如果我尝试对10,000行进行相同的操作,则不需要一秒钟。想象它需要一秒钟,我希望整个动作大约需要 1秒* 40 = 40秒。 另一方面,如果它是一个纯粹的内存问题,我会预料到堆栈溢出并没有发生。因此,执行每个连接所需的时间似乎呈指数级增长。

我的问题:

  1. 有人会解释我为什么会这样吗?
  2. 有没有人有任何改善此代码性能的建议?也许我应该将串联分成几个字符串(比如每个10k行的40个字符串)并在稍后阶段将它们连接起来?

2 个答案:

答案 0 :(得分:2)

除了您在评论中看到的方式之外,您可能还想尝试“数组”方法

可以通过根据需要将其分成尽可能多的子数组来克服其最大数组大小限制,如下所示:

Private Function buildFileText() As String    
    Dim ss As String
    Dim count As Long

    With Worksheets("FileContent")
        With .Range("A1", .Cells(.Rows.count, 1).End(xlUp))
            Do While .count - count > 24684
                ss = ss & Join(Application.Transpose(.Offset(count).Resize(24684).Value), vbNewLine)
                count = count + 24684
            Loop
            buildFileText = ss & Join(Application.Transpose(.Offset(count).Resize(.count - count).Value), vbNewLine)                
        End With
    End With        
End Function

答案 1 :(得分:2)

初始化巨大的字符串不需要很长时间。

字符串不可调整大小。将两个字符串连接在一起时,会创建一个Temp字符串来保存这两个值。然后将值分配给Temp字符串,然后将目标字符串替换为临时字符串。

为什么前10,00行需要不到一秒钟,389,256需要1小时44分钟?

"因此,执行每个连接所需的时间似乎呈指数级增长" - 它实际上以一致的速度增长。如果它呈指数增长,Excel会很快崩溃。

但问题是它正在增长,每次连接时都需要更多的内存来创建新的更大的字符串。

我们可以做些什么来提高绩效?

在您的情况下,我会使用MSXML2来创建XML输出。详细记录将使您的代码易于扩展。

第二个选项是实施String Builder PatternString Builders通过初始化一个非常大的输出字符串并将新字符串写入输出字符串中的下一个位置来减少连接数。

我对Excel vba xml parsing performance的回答显示了如何使用String BUilder Pattern将Excel表格公开为XML。 Parfiat对同一问题的回答演示了如何使用MSXML2创建XML文件。