Results 1 to 20 of 20

Thread: What is the fastest way to append to a string?

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Jul 2003
    Posts
    1,269

    What is the fastest way to append to a string?

    string = string & new_data

    takes an enormously long time to execute..
    it works very well for small strings and small loops but if you have to do a loop 180,000 times appending to a string, it takes FOREVER using the above code.

    is there a faster method i dont know about?

  2. #2
    PowerPoster Ellis Dee's Avatar
    Join Date
    Mar 2007
    Location
    New England
    Posts
    3,530

    Re: What is the fastest way to append to a string?

    Redimension a byte array with as many elements as there will be characters. For each new character to append, write its Asc() code to the next unused array element. When you are finished "appending" characters, convert the byte array to a string:

    strResult = StrConv(bytArray, vbUnicode)

  3. #3

    Thread Starter
    Frenzied Member
    Join Date
    Jul 2003
    Posts
    1,269

    Re: What is the fastest way to append to a string?

    Quote Originally Posted by Ellis Dee
    Redimension a byte array with as many elements as there will be characters. For each new character to append, write its Asc() code to the next unused array element. When you are finished "appending" characters, convert the byte array to a string:

    strResult = StrConv(bytArray, vbUnicode)
    hmm
    I dont know how many characters there will be though..

  4. #4
    PowerPoster Ellis Dee's Avatar
    Join Date
    Mar 2007
    Location
    New England
    Posts
    3,530

    Re: What is the fastest way to append to a string?

    Then redimennsion blocks at a time. If you're expecting 180k, I'd start it off at 200k and make subsequent blocks 10k each.

    Don't forget to size it back down to the exact length before converting it to a string.

  5. #5
    "Digital Revolution"
    Join Date
    Mar 2005
    Posts
    4,471

    Re: What is the fastest way to append to a string?

    Quote Originally Posted by VaxoP
    string = string & new_data

    takes an enormously long time to execute..
    it works very well for small strings and small loops but if you have to do a loop 180,000 times appending to a string, it takes FOREVER using the above code.

    is there a faster method i dont know about?
    This class was made just for this.

    http://www.vbforums.com/attachment.p...6&d=1175915331

    vb Code:
    1. Dim myString As New clsConcat
    2.  
    3. myString.SConcat "this "
    4. myString.SConcat "is "
    5. myString.SConcat "a "
    6. myString.SConcat "test."
    7.  
    8. MsgBox myString.GetString
    9. Set myString = Nothing

    It's a huge improvement over normal string concatenation and speeds large string operations up a lot.

    If speed is really an issue I usually try to rewrite the code to work with byte arrays.

  6. #6

    Thread Starter
    Frenzied Member
    Join Date
    Jul 2003
    Posts
    1,269

    Re: What is the fastest way to append to a string?

    Quote Originally Posted by DigiRev
    This class was made just for this.

    http://www.vbforums.com/attachment.p...6&d=1175915331

    vb Code:
    1. Dim myString As New clsConcat
    2.  
    3. myString.SConcat "this "
    4. myString.SConcat "is "
    5. myString.SConcat "a "
    6. myString.SConcat "test."
    7.  
    8. MsgBox myString.GetString
    9. Set myString = Nothing

    It's a huge improvement over normal string concatenation and speeds large string operations up a lot.

    If speed is really an issue I usually try to rewrite the code to work with byte arrays.
    Neat that runs about 16% faster (up to 60% faster for really big strings)
    How can you do it with byte arrays? Do you have any sample code i could peek at (ive never used em before)

  7. #7
    PowerPoster Ellis Dee's Avatar
    Join Date
    Mar 2007
    Location
    New England
    Posts
    3,530

    Re: What is the fastest way to append to a string?

    I'd be happy to set up a working example for you, but first I need to know where the input text is coming from. Can you set up a shell that skips the concatenation part?

    The class isn't much faster because each call forces a redimension. A byte array approach will be like greased lightning in comparison.

  8. #8

    Thread Starter
    Frenzied Member
    Join Date
    Jul 2003
    Posts
    1,269

    Re: What is the fastest way to append to a string?

    Well right now the program just goes through an array of 100,000 items, does a simple check on them (if instr(array,something)) and appends the value to a string.

    for x = 0 to ubound(array) - 1
    if instr(array(x),"something") then myString = myString & array(x) & vbcrlf
    next


    like that

  9. #9
    PowerPoster techgnome's Avatar
    Join Date
    May 2002
    Posts
    34,687

    Re: What is the fastest way to append to a string?

    Ick! .... it could be the array itself and not the concatenation that's the issue....

    try this see if there's any improvement:

    Code:
    Dim MyText As String
    for x = 0 to ubound(array) - 1
      MyText = array(x)
    if instr(MyText,"something") then myString = myString & MyText & vbcrlf
    next
    It could also be the Instr that's innefficient.

    -tg
    * I don't respond to private (PM) requests for help. It's not conducive to the general learning of others.*
    * I also don't respond to friend requests. Save a few bits and don't bother. I'll just end up rejecting anyways.*
    * How to get EFFECTIVE help: The Hitchhiker's Guide to Getting Help at VBF - Removing eels from your hovercraft *
    * How to Use Parameters * Create Disconnected ADO Recordset Clones * Set your VB6 ActiveX Compatibility * Get rid of those pesky VB Line Numbers * I swear I saved my data, where'd it run off to??? *

  10. #10

    Thread Starter
    Frenzied Member
    Join Date
    Jul 2003
    Posts
    1,269

    Re: What is the fastest way to append to a string?

    Quote Originally Posted by techgnome
    Ick! .... it could be the array itself and not the concatenation that's the issue....

    try this see if there's any improvement:

    Code:
    Dim MyText As String
    for x = 0 to ubound(array) - 1
      MyText = array(x)
    if instr(MyText,"something") then myString = myString & MyText & vbcrlf
    next
    It could also be the Instr that's innefficient.

    -tg
    Thats not it..
    I tried removing my concat line altogether and doing something else with the data (adding to listbox for example) and speed improved significantly.

    It is also that line that I changed when I was using the class module provided here.. which showed a huge performance increase when switching.

    The bottleneck without question is myString = myString & array(x) & vbcrlf

  11. #11
    Frenzied Member
    Join Date
    Jun 2006
    Posts
    1,098

    Re: What is the fastest way to append to a string?

    This is similar to Ellis Dee's suggestion, but with much less hassle.
    Be sure to replace the array variable in this code with the actual array.
    Code:
    Dim strMyString As String ' The string
    Dim strArray() As String  ' Stand-in for the array
    Dim strBuffer As String   ' The buffer
    Dim lngBuffSize As Long   ' Amount of space to buffer
    Dim lngPos As Long        ' Position within the buffer
    Dim lngLen As Long        ' Lenght of string to copy to buffer
    Dim i As Long
    
    ' Try to pick a length that is 20% to 60% of the length of the final string.
    ' Too long wastes memory. Too short slows it down with extra extensions.
    lngBuffSize = 500
    
    ' Set the initial size of the buffer
    lngLen = Len(strMyString)
    strBuffer = Space(lngLen + lngBuffSize)
    
    ' Copy strMyString to the buffer
    Mid(strBuffer, 1, lngLen) = strMyString
    lngPos = Len(strMyString) + 1
    
    For i = 0 To UBound(strArray) - 1
      If InStr(strArray(i), "something") > 0 Then
        lngLen = Len(strArray(i))
        
        ' Extend the length of the buffer for more space, if needed.
        If lngPos + lngLen + 2 > Len(strBuffer) Then ' + 2 for vbCrLf
          strBuffer = strBuffer & Space(lngBuffSize + lngLen + 2)
        End If
        
        ' Copy the array element to the buffer.
        Mid(strBuffer, lngPos, lngLen) = strArray(i)
        lngPos = lngPos + lngLen
        
        ' Copy vbCrLf to the buffer.
        Mid(strBuffer, lngPos, 2) = vbCrLf
        lngPos = lngPos + 2
      End If
    Next i
    
    ' Copy the buffer to strMyString
    strMyString = Left$(strBuffer, lngPos - 1)
    
    ' Clear the buffer
    strBuffer = ""
    Edit: After looking at the class that DigiRev posted, I see that this code is doing the exact same thing as the class. This inline code will run faster, though, due to the overhead of repeatedly calling a function and the general nature of classes.

    Regarding the class, there is a bug. The SetString method needs to update lOffset. Add the line lOffset = Len(Source).
    Last edited by Logophobic; Aug 20th, 2007 at 06:42 PM.

  12. #12
    PowerPoster Ellis Dee's Avatar
    Join Date
    Mar 2007
    Location
    New England
    Posts
    3,530

    Re: What is the fastest way to append to a string?

    Quote Originally Posted by VaxoP
    Well right now the program just goes through an array of 100,000 items, does a simple check on them (if instr(array,something)) and appends the value to a string.
    This changes the ballgame entirely. (And you're right about the concatenation being the bottleneck.) The magic bullet for speeding character concatenation is to use byte arrays. But you aren't concatenating characters, you're concatenating strings. The magic bullet for that is the Join() function.

    Here's what you want to do:
    • Start by creating a second (empty) string array the same size as the original
    • Maintain a "current position" counter variable identifying the next available slot in the new array
    • Iterate the original array; each time you find a hit, copy it to the next available slot in the new array, updating your counter
    • When you are all done, set your destination string equal to Join(NewArray, "")
    It'll be as fast as it gets.

    You can ignore the extraneous array elements that don't end up getting used for simplicity. If you're as anal as I am, you'll probably want to ReDim Preserve the second array to make it only hold the number of elements actually used. (I suspect this might actually slow it down, though.)

    EDIT: Logo's method will be very fast, but I believe this will be significantly faster than that.
    Last edited by Ellis Dee; Aug 20th, 2007 at 07:11 PM.

  13. #13
    PowerPoster Ellis Dee's Avatar
    Join Date
    Mar 2007
    Location
    New England
    Posts
    3,530

    Re: What is the fastest way to append to a string?

    Also, you may be able to do it as a one-liner using the Filter() function.

    EDIT: I'm setting up a benchmark to test how fast each method is. I'll post the code and results in a bit.
    Last edited by Ellis Dee; Aug 20th, 2007 at 07:22 PM.

  14. #14
    PowerPoster Ellis Dee's Avatar
    Join Date
    Mar 2007
    Location
    New England
    Posts
    3,530

    Re: What is the fastest way to append to a string?

    Logo's buffer method is extremely fast, but it has a bug somewhere in it where it's copying too much text. Not sure what the deal is. (EDIT: I figured it out and corrected; see next post.) Other than that, the main problem is that you have to specify a correct buffer size to get the best results, which is problematic for a generic implementation.

    I took his idea and ran with it, but improved it to not require a buffer size at all. Instead, I run through the original array and add up the sizes of all the matches, create the output string once with the correct size, then run through the array again pasting each match into the output string. I use a secondary array of booleans to track which array elements were matches so I don't have to repeat the criteria.

    Both these methods are faster than anything else, but not by much. My Join() method is pretty peppy and quite flexible, but it's not as good as either buffer method in any way. The native Filter() command is by far the easiest to implement if your criteria boils down to a simple InStr() call. Try this:

    OutputString = Join(Filter(StringArray, SearchString), "")

    One line of code and quite fast, so this is probably the way you want to go. For completeness, here's the benchmark program and the results. You should be able to figure out how to use any of the methods you need should your criteria expand beyond a simple InStr() call.

    To run the benchmark, create a project, add a multi-line textbox and a command button to the form, and paste this code (reply with quote and copy it from the reply window) into the form's module:
    vb Code:
    1. Option Explicit
    2.  
    3. Private Const ArraySize = 99999
    4. Private Const Delimiter = vbNewLine
    5.  
    6. Public Function FilterArray(pstrArray() As String, pstrFilter As String, pstrDelimiter As String)
    7.     Dim strDest() As String
    8.     Dim lngPos As Long
    9.     Dim i As Long
    10.     Dim iMin As Long
    11.     Dim iMax As Long
    12.    
    13.     iMin = LBound(pstrArray)
    14.     iMax = UBound(pstrArray)
    15.     ReDim strDest(iMin To iMax)
    16.     lngPos = iMin
    17.     For i = iMin To iMax
    18.         If InStr(pstrArray(i), pstrFilter) <> 0 Then
    19.             strDest(lngPos) = pstrArray(i)
    20.             lngPos = lngPos + 1
    21.         End If
    22.     Next
    23.     ReDim Preserve strDest(iMin To lngPos - 1)
    24.     FilterArray = Join(strDest, pstrDelimiter)
    25.     Erase strDest
    26. End Function
    27.  
    28. Public Function EllisBufferMethod(pstrArray() As String, pstrFilter As String, pstrDelimiter As String)
    29.     Dim strBuffer As String
    30.     Dim lngLen As Long
    31.     Dim lngPos As Long
    32.     Dim blnMatch() As Boolean
    33.     Dim i As Long
    34.     Dim iMin As Long
    35.     Dim iMax As Long
    36.    
    37.     iMin = LBound(pstrArray)
    38.     iMax = UBound(pstrArray)
    39.     ReDim blnMatch(iMin To iMax)
    40.     For i = iMin To iMax
    41.         If InStr(pstrArray(i), pstrFilter) Then
    42.             lngLen = lngLen + Len(pstrArray(i)) + Len(pstrDelimiter)
    43.             blnMatch(i) = True
    44.         End If
    45.     Next
    46.     strBuffer = Space(lngLen + 1)
    47.     lngPos = 1
    48.     For i = iMin To iMax
    49.         If blnMatch(i) Then
    50.             Mid$(strBuffer, lngPos, Len(pstrArray(i))) = pstrArray(i)
    51.             lngPos = lngPos + Len(pstrArray(i))
    52.             Mid$(strBuffer, lngPos, Len(pstrDelimiter)) = pstrDelimiter
    53.             lngPos = lngPos + Len(pstrDelimiter)
    54.         End If
    55.     Next
    56.     EllisBufferMethod = Left$(strBuffer, lngLen - Len(pstrDelimiter))
    57.     strBuffer = ""
    58.     Erase blnMatch
    59. End Function
    60.  
    61. Public Function LogoBufferMethod(pstrArray() As String, pstrFilter As String, pstrDelimiter As String)
    62.     Dim strBuffer As String   ' The buffer
    63.     Dim lngBuffSize As Long   ' Amount of space to buffer
    64.     Dim lngPos As Long        ' Position within the buffer
    65.     Dim lngLen As Long        ' Lenght of string to copy to buffer
    66.     Dim i As Long
    67.     Dim lngDelimiterLen As Long
    68.    
    69.     ' Try to pick a length that is 20% to 60% of the length of the final string.
    70.     ' Too long wastes memory. Too short slows it down with extra extensions.
    71.     lngBuffSize = 80000 ' ~40%
    72.    
    73.     ' Set the initial size of the buffer
    74.     strBuffer = Space(lngBuffSize)
    75.    
    76.     lngPos = 1
    77.     lngDelimiterLen = Len(pstrDelimiter)
    78.    
    79.     For i = 0 To UBound(pstrArray) - 1
    80.         If InStr(pstrArray(i), pstrFilter) > 0 Then
    81.             lngLen = Len(pstrArray(i))
    82.            
    83.             ' Extend the length of the buffer for more space, if needed.
    84.             If lngPos + lngLen + lngDelimiterLen > Len(strBuffer) Then
    85.                 strBuffer = strBuffer & Space(lngBuffSize + lngLen + lngDelimiterLen)
    86.             End If
    87.            
    88.             ' Copy the array element to the buffer.
    89.             Mid(strBuffer, lngPos, lngLen) = pstrArray(i)
    90.             lngPos = lngPos + lngLen
    91.             ' Copy delimiter to the buffer.
    92.             Mid(strBuffer, lngPos, lngDelimiterLen) = pstrDelimiter
    93.             lngPos = lngPos + lngDelimiterLen
    94.         End If
    95.     Next i
    96.    
    97.     ' Copy the buffer to strMyString
    98.     LogoBufferMethod = Left$(strBuffer, lngPos - lngDelimiterLen - 1)
    99.    
    100.     ' Clear the buffer
    101.     strBuffer = ""
    102. End Function
    103.  
    104. Private Sub Command1_Click()
    105.     Me.MousePointer = vbHourglass
    106.     Me.Text1.Text = Sample()
    107.     Me.MousePointer = vbNormal
    108. End Sub
    109.  
    110. Public Function Sample() As String
    111.     Dim strArray(ArraySize) As String
    112.     Dim i As Long
    113.     Dim strOutput As String
    114.     Dim sngStart As Single
    115.     Dim strMessage As String
    116.    
    117.     For i = 0 To ArraySize
    118.         strArray(i) = i
    119.     Next
    120.     ' String concatenation
    121.     sngStart = Timer
    122.     For i = 0 To ArraySize
    123.         If InStr(strArray(i), "1") <> 0 Then strOutput = strOutput & strArray(i) & Delimiter
    124.     Next
    125.     strMessage = strMessage & "Concatenation: " & Format(Timer - sngStart, "0.000") & " seconds (" & Len(strOutput) & " characters)" & vbCrLf
    126.     strOutput = ""
    127.     ' Logo's string buffer method
    128.     sngStart = Timer
    129.     strOutput = LogoBufferMethod(strArray, "1", Delimiter)
    130.     strMessage = strMessage & "Logo's buffer method: " & Format(Timer - sngStart, "0.000") & " seconds (" & Len(strOutput) & " characters)" & vbCrLf
    131.     strOutput = ""
    132.     ' Ellis's string buffer method
    133.     sngStart = Timer
    134.     strOutput = EllisBufferMethod(strArray, "1", Delimiter)
    135.     strMessage = strMessage & "Ellis's buffer method: " & Format(Timer - sngStart, "0.000") & " seconds (" & Len(strOutput) & " characters)" & vbCrLf
    136.     strOutput = ""
    137.     ' Join
    138.     sngStart = Timer
    139.     strOutput = FilterArray(strArray, "1", Delimiter)
    140.     strMessage = strMessage & "Join(): " & Format(Timer - sngStart, "0.000") & " seconds (" & Len(strOutput) & " characters)" & vbCrLf
    141.     strOutput = ""
    142.     ' FIlter
    143.     sngStart = Timer
    144.     strOutput = Join(Filter(strArray, "1"), Delimiter)
    145.     strMessage = strMessage & "Filter(): " & Format(Timer - sngStart, "0.000") & " seconds (" & Len(strOutput) & " characters)" & vbCrLf
    146.     Sample = strMessage
    147. End Function
    You should run it from a compiled exe to get realistic results. On my old, slow machine, the results are:

    Concatenation: 253.289 seconds (282927 characters)
    Logo's buffer method: 0.070 seconds (282925 characters)
    Ellis's buffer method: 0.086 seconds (282925 characters)
    Join(): 0.203 seconds (282925 characters)
    Filter(): 0.227 seconds (282925 characters)
    Last edited by Ellis Dee; Aug 20th, 2007 at 08:46 PM.

  15. #15
    PowerPoster Ellis Dee's Avatar
    Join Date
    Mar 2007
    Location
    New England
    Posts
    3,530

    Re: What is the fastest way to append to a string?

    I figured out the "bug" in Logo's code, and it isn't a bug at all. (I'm a fool for doubting him.) He's adding a delimiter between each string, which makes a ton of sense.

    I'll update the code above to handle this, as well as the results. Shouldn't take long.

    EDIT: I finished the changes. You can specify an empty string as the delimiter if you don't want to use one. While Logo's basic method is the fastest, it requires prior knowledge of the results.
    Last edited by Ellis Dee; Aug 20th, 2007 at 08:48 PM.

  16. #16
    PowerPoster
    Join Date
    Nov 2002
    Location
    Manila
    Posts
    7,629

    Re: What is the fastest way to append to a string?

    Why isn't the data stored in a database table?

  17. #17
    PowerPoster Ellis Dee's Avatar
    Join Date
    Mar 2007
    Location
    New England
    Posts
    3,530

    Re: What is the fastest way to append to a string?

    On an unrelated note, I'd like to point out that if there was ever compelling evidence that you never need to store the results of Len() to a variable for speed reasons, my buffer method code is it.

  18. #18
    Frenzied Member
    Join Date
    Jun 2006
    Posts
    1,098

    Re: What is the fastest way to append to a string?

    My PC is a bit faster, so I used a high-resolution timer.
    The percentages are buffer size relative to total output length.
    The times are an average over 10 calls.
    Code:
    Output string length: 282,925 characters
    Logo:
     100%   11.45 ms
      53%   11.83 ms
      35%   11.74 ms
      27%   12.03 ms
      21%   12.23 ms
      17%   13.02 ms
      11%   27.64 ms
    
    Ellis:  12.57 ms
    I also increased the array size by a factor of 10.
    Code:
    Output string length: 3,703,789 characters
    Logo:
     100%  132.62 ms
      54%  143.39 ms
      34%  155.08 ms
      27%  157.05 ms
      20%  160.27 ms
      17%  170.40 ms
      11%  364.39 ms
    
    Ellis: 144.46 ms
    As you can see, the difference between Ellis Dee's pre-pass method and a good estimate of the space needed is negligible. However, a low estimate causes a significant loss of efficiency, so your best bet would be to use his code.

  19. #19

    Thread Starter
    Frenzied Member
    Join Date
    Jul 2003
    Posts
    1,269

    Re: What is the fastest way to append to a string?

    neat thanks
    so is the only drawback to using a large space the memory usage?

  20. #20
    PowerPoster Ellis Dee's Avatar
    Join Date
    Mar 2007
    Location
    New England
    Posts
    3,530

    Re: What is the fastest way to append to a string?

    Quote Originally Posted by VaxoP
    neat thanks
    so is the only drawback to using a large space the memory usage?
    Pretty much, yeah. Most of that extra space can be removed easily enough by using the return value directly instead of using a local string. That means that there is no extra space being used for a temporary string, but it still requires the overhead of the boolean array. I've renamed it to the more generic FilterArrayToString():
    Code:
    Public Function FilterArrayToString(pstrArray() As String, pstrFilter As String, pstrDelimiter As String) As String
        Dim lngLen As Long
        Dim lngPos As Long
        Dim blnMatch() As Boolean
        Dim i As Long
        Dim iMin As Long
        Dim iMax As Long
        
        iMin = LBound(pstrArray)
        iMax = UBound(pstrArray)
        ReDim blnMatch(iMin To iMax)
        For i = iMin To iMax
            If InStr(pstrArray(i), pstrFilter) <> 0 Then
                lngLen = lngLen + Len(pstrArray(i)) + Len(pstrDelimiter)
                blnMatch(i) = True
            End If
        Next
        FilterArrayToString = Space(lngLen + 1)
        lngPos = 1
        For i = iMin To iMax
            If blnMatch(i) Then
                Mid$(FilterArrayToString, lngPos, Len(pstrArray(i))) = pstrArray(i)
                lngPos = lngPos + Len(pstrArray(i))
                Mid$(FilterArrayToString, lngPos, Len(pstrDelimiter)) = pstrDelimiter
                lngPos = lngPos + Len(pstrDelimiter)
            End If
        Next
        FilterArrayToString = Left$(FilterArrayToString, lngLen - Len(pstrDelimiter))
        Erase blnMatch
    End Function
    This approach is nice because it is very customizable, but I'm still a fan of the one-liner approach.
    Last edited by Ellis Dee; Aug 21st, 2007 at 12:10 AM. Reason: Added explicit "As String" return value type to function prototype

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width