A Faster Approach
One solution to this dilemma is to unlink the memory reallocation from the string-concatenation operation, allowing the developer to implement a more efficient algorithm. This solution has been implemented in an in-process VB COM component (ConCat.dll) that encapsulates string storage and the required concatenation functionality. The source code for this VB 6.0 class is presented in Listing 1.
Listing 1: Source Code for the ConCat Class
' =================================================== ' ' CConCat: ' ' This class provides an alternative, more efficient ' ' approach to string concatenation. Concatenation of ' ' large VB strings requires reallocation of memory at ' ' each concatenation. The approach used here reduces ' ' the number of memory reallocations. ' ' ' ' Empirical results indicate that this is faster than ' ' standard VB concatenation for strings greater than ' ' 1K in length, with the level of improvement growing ' ' with the size of the string. ' ' ' ' Public Property Get Text() as String ' ' Public Property Let Text(ByVal sNew as String) ' ' Public Property Get Increment() as Long ' ' Public Property Let Increment(ByVal nNew as Long) ' ' Public Property Get Length() as Long ' ' Public Sub Cat(ByVal sNewPart as String) ' ' ' ' Copyright © 2001 by Scott R. Loban. ' ===================================================== ' Private m_sText As String '-- Text being stored in object Private m_nPointer As Long '-- Pointer to end of string Private m_nIncrement As Long '-- Number of bytes to add at a time '--- Return the Text Stored in the Class ---' Public Property Get Text() As String Dim ErNum, ErDesc, ErSrc On Error GoTo ErrHandler Text = Left$(m_sText, m_nPointer) Exit Property ErrHandler: ErNum = Err.Number ErDesc = Err.Description ErSrc = Err.Source & ", CConCat.Text" Err.Raise ErNum, ErSrc, ErDesc End Property '--- Set (replace) the Text Stored in the Class ---' Public Property Let Text(ByVal new_sText As String) Dim ErNum, ErDesc, ErSrc On Error GoTo ErrHandler m_sText = new_sText m_nPointer = Len(m_sText) Exit Property ErrHandler: ErNum = Err.Number ErDesc = Err.Description ErSrc = Err.Source & ", CConCat.Text" Err.Raise ErNum, ErSrc, ErDesc End Property '--- Return the Current Reallocation Increment ---' Public Property Get Increment() As Long Dim ErNum, ErDesc, ErSrc On Error GoTo ErrHandler Increment = m_nIncrement Exit Property ErrHandler: ErNum = Err.Number ErDesc = Err.Description ErSrc = Err.Source & ", CConCat.Increment" Err.Raise ErNum, ErSrc, ErDesc End Property '--- Set a New Reallocation Increment ---' Public Property Let Increment(ByVal new_nIncrement As Long) Dim ErNum, ErDesc, ErSrc On Error GoTo ErrHandler If new_nIncrement > 0 Then m_nIncrement = new_nIncrement End If Exit Property ErrHandler: ErNum = Err.Number ErDesc = Err.Description ErSrc = Err.Source & ", CConCat.Increment" Err.Raise ErNum, ErSrc, ErDesc End Property '--- Return the Length of the Text Stored in the Class ---' Public Property Get Length() As Long Dim ErNum, ErDesc, ErSrc On Error GoTo ErrHandler Length = m_nPointer Exit Property ErrHandler: ErNum = Err.Number ErDesc = Err.Description ErSrc = Err.Source & ", CConCat.Length" Err.Raise ErNum, ErSrc, ErDesc End Property '--- Concatenate the Text Stored in the Class and a Passed String ---' Public Sub Cat(ByVal sNewPart As String) Dim nLen As Long Dim ErNum, ErDesc, ErSrc On Error GoTo ErrHandler nLen = Len(sNewPart) If (m_nPointer + nLen) >= Len(m_sText) Then '-- Not enough room If nLen > m_nIncrement Then '-- Need nLen more bytes m_sText = m_sText & Space$(nLen) '-- Do Reallocation Else '-- Add more space m_sText = m_sText & Space$(m_nIncrement) '-- Do Reallocation End If End If '--- Store the NewPart in the String ---' Mid$(m_sText, m_nPointer + 1, nLen) = sNewPart '--- Adjust the Pointer ---' m_nPointer = m_nPointer + nLen Exit Sub ErrHandler: ErNum = Err.Number ErDesc = Err.Description ErSrc = Err.Source & ", CConCat.Cat" Err.Raise ErNum, ErSrc, ErDesc End Sub Private Sub Class_Initialize() Dim ErNum, ErDesc, ErSrc On Error GoTo ErrHandler m_nIncrement = 50000 m_sText = "" m_nPointer = 0 Exit Sub ErrHandler: ErNum = Err.Number ErDesc = Err.Description ErSrc = Err.Source & ", CConCat.Class_Initialize" Err.Raise ErNum, ErSrc, ErDesc End Sub
ConCat works by preallocating a larger initial block of memory for the text being stored and then making memory reallocations explicitand, therefore, controllable. A public method (Cat) is used to concatenate a specified string to the existing text by using the VB Mid$ function to write the string into the text storage. The component maintains an index that acts as a pointer to the end of the string being stored. The Cat method also determines when reallocation is necessary and how much additional memory to allocate. A set of public properties provides access to the text and its length.
Table 1 illustrates the results of empirical testing to compare the performance of Append and the ConCat class. As the table indicates, the approach implemented in ConCat can provide performance that is much faster.
Table 1 Comparison of Append and ConCat Performance
Test Case |
Append (&) |
ConCat |
1000 x 10 characters |
0* |
0* |
2000 x 10 characters |
2 |
0* |
4000 x 10 characters |
8 |
0* |
8000 x 10 characters |
35 |
0* |
16,000 x 10 characters |
151 |
0* |
32,000 x 10 characters |
619 |
0* |
64,000 x 10 characters |
|
1 |
128,000 x 10 characters |
|
1 |
256,000 x 10 characters |
|
3 |
Although the concatenation operator built into Visual Basic 6.0 is great for most tasks, this alternative approach offers significant improvement for the special case of concatenating many small strings into a large string, such as when dynamically generating HTML or XML text streams.