Dynamic arrays in UDTs (once again)
Optimizing the execution speed in case of dynamic memory allocation for character data of var-len string:
- To limit the occurrence of memory allocation calls (which are eating CPU time), every change in the number of required elements, do not fit exactly the size of the allocated memory.
- The memory allocation is done in successive steps (memory allocation per block), so that it can withstand small changes in the number of elements without changing the memory allocation.
FreeBASIC handles variable-length string, using a string descriptor (12 bytes, from @string):
- address pointing to the string character data,
- size actually used by the string,
- string size available in memory.
This small program allows you to monitor the memory management by FreeBASIC for var-len string:
- first half of the window corresponding to each direct string assignment for several different sizes (string = other_string),
- second half of the window corresponding to one string successively continuous inflating by the right (string = string + other_string) then continuous deflating by the right (string = Left(string, N))
We can see that management is not so trivial as that.
Code: Select all
Dim s As String
Dim t(639, 2) As Integer
Screen 12
Print
Color 7
Print " String-data pointer: 'Cast(Integer Ptr, @string)[0]' modulo &h100"
Color 13
Print " Used string size inside allocated memory: 'Cast(Integer Ptr, @string)[1]'"
Color 10
Print " Available string size in allocated memory: 'Cast(Integer Ptr, @string)[2]'"
Print
Color 15
Print " Direct string assignments Continuous inflating then deflating"
For k As Integer = 0 To 639
t(k, 0) = Cast(Integer Ptr, @s)[0]
t(k, 1) = Cast(Integer Ptr, @s)[1]
t(k, 2) = Cast(Integer Ptr, @s)[2]
If k < 159 Then
s = String(2 * k, 0)
Elseif k < 319 Then
s = String(158 * 4 - 2 * k, 0)
Elseif k < 479 Then
s = s + String(2, 0)
' s = s + Chr(0) + Chr(0)
' s = s + (Chr(0) + Chr(0))
' s = s + String(2*(k - 318) - Len(s), 0)
' s = s + String((2*(k - 318) - Len(s)), 0)
' *@s = *@s + String(2, 0)
Else
dim I As Integer = Len(s) - 2
s = Left(s, I)
End If
Next K
Pset(0, 479), 8
For k As Integer = 0 To 639
Line -(k, 479 - (t(k, 0) And &hFF)), 8
Next k
Pset(0, 479), 13
For k As Integer = 0 To 639
Line -(k, 479 - t(k, 1)), 13
Next k
Pset(0, 479), 10
For k As Integer = 0 To 639
Line -(k, 479 - t(k, 2)), 10
Next k
Sleep
- We can verify than the expression 'string = string + other_string' (continuous inflating by the right) is well optimized by the compiler (one fix pointer during each step of memory size)
- But the expression 'string = Left(string, N)' (continuous deflating by the right) is not optimized by the compiler. Why?
Remarks:
- The expression 'string = string + other_string' (continuous inflating by the right) is well optimized by the compiler only for simple 'other_string' expressions:
for example 'Chr(0)' or 'String(N, 0)', but not for more complex expressions as for example 'String(N - Len(s), 0)'.
(in the program above, we can modify the line 25 with several other proposed syntaxes and see the result on the memory pointer values)
- The compiler optimization works only with direct string variable access, and not with pointer:
*(@string) = *(@string) + String(2, 0) is not optimized by the compiler.
- The compiler optimization works only with direct string variable access, and not with reference:
In a procedure, syntax with string passed by reference is not optimized by the compiler:
a work around is to swap it with a local string, to inflate the local string, then to swap again with the passed reference.
The previous program is modified in order to introduce a procedure 'Inflating_by_the_right' processing the string (passed by reference), and we can test the result on the memory pointer values, using or not using the 'Swap' method:
Code: Select all
Sub Inflating_by_the_right (Byref s As String, Byval k As Integer)
' s = s + String(k, 0)
Dim s0 As String
Swap s, s0
s0 = s0 + String(k, 0)
Swap s0, s
End Sub
Dim s As String
Dim t(639, 2) As Integer
Screen 12
Print
Color 7
Print " String-data pointer: 'Cast(Integer Ptr, @string)[0]' modulo &h100"
Color 13
Print " Used string size inside allocated memory: 'Cast(Integer Ptr, @string)[1]'"
Color 10
Print " Available string size in allocated memory: 'Cast(Integer Ptr, @string)[2]'"
Print
Color 15
Print " Direct string assignments Continuous inflating then deflating"
For k As Integer = 0 To 639
t(k, 0) = Cast(Integer Ptr, @s)[0]
t(k, 1) = Cast(Integer Ptr, @s)[1]
t(k, 2) = Cast(Integer Ptr, @s)[2]
If k < 159 Then
s = String(2 * k, 0)
Elseif k < 319 Then
s = String(158 * 4 - 2 * k, 0)
Elseif k < 479 Then
' s = s + String(2, 0)
' s = s + Chr(0) + Chr(0)
' s = s + (Chr(0) + Chr(0))
' s = s + String(2*(k - 318) - Len(s), 0)
' s = s + String((2*(k - 318) - Len(s)), 0)
' *@s = *@s + String(2, 0)
Inflating_by_the_right(s, 2)
Else
dim I As Integer = Len(s) - 2
s = Left(s, I)
End If
Next K
Pset(0, 479), 8
For k As Integer = 0 To 639
Line -(k, 479 - (t(k, 0) And &hFF)), 8
Next k
Pset(0, 479), 13
For k As Integer = 0 To 639
Line -(k, 479 - t(k, 1)), 13
Next k
Pset(0, 479), 10
For k As Integer = 0 To 639
Line -(k, 479 - t(k, 2)), 10
Next k
Sleep