For more information, see discussion at:
https://www.freebasic.net/forum/viewtopic.php?t=33256
Without 'Any' initializer, 'Redim' / 'Erase' on numeric datatype induces a run time similar to the one with 'Callocate' / 'Deallocate', while 'New[]' / 'Delete[]' induces a run time 1000 times greater for the example below.
It is why I consider that it is a non-optimization bug for 'New[]'/'Delete[]' when datatype has no 'Constructor'/'Destructor' (and without '{Any}' initializer).
Example (compile without code optimization option):
Dim As Double t
Print "MEMORY (LONGS) NOT INITIALIZED ON SIZING"
Print "----------------------------------------"
Print
Print "Memory sizing (sizing then freeing) using 'Allocate'/'Deallocate':"
t = Timer
For I As Integer = 1 To 10
Dim As Long Ptr p = Allocate(10000000 * Sizeof(Long))
Deallocate(p)
Next I
t = Timer - t
Print " total (sizing + freeing):"; t; " s."
Print
Print "Memory sizing (sizing then freeing) using 'REDIM+=ANY'/'ERASE ':"
t = Timer
For I As Integer = 1 To 10
Redim As Long a(10000000 - 1) = Any
Erase a
Next I
t = Timer - t
Print " total (sizing + freeing):"; t; " s."
Print
Print "Memory sizing (sizing then freeing) using 'New[]+{ANY}'/'DELETE[]':"
t = Timer
For I As Integer = 1 To 10
Dim As Long Ptr p = New Long[10000000] {Any}
Delete[](p)
Next I
t = Timer - t
Print " total (sizing + freeing):"; t; " s."
Print
Print "============================================================================="
Print
Print "MEMORY (LONGS) INITIALIZED TO ZERO ON SIZING"
Print "--------------------------------------------"
Print
Print "Memory sizing (sizing then freeing) using 'Callocate'/'Deallocate':"
t = Timer
For I As Integer = 1 To 10
Dim As Long Ptr p = Callocate(10000000 * Sizeof(Long))
Deallocate(p)
Next I
t = Timer - t
Print " total (sizing + freeing):"; t; " s."
Print
Print "Memory sizing (sizing then freeing) using 'REDIM'/'ERASE ':"
t = Timer
For I As Integer = 1 To 10
Redim As Long a(10000000 - 1)
Erase a
Next I
t = Timer - t
Print " total (sizing + freeing):"; t; " s."
Print
Print "Memory sizing (sizing then freeing) using 'New[]'/'DELETE[]':"
t = Timer
For I As Integer = 1 To 10
Dim As Long Ptr p = New Long[10000000]
Delete[](p)
Next I
t = Timer - t
Print " total (sizing + freeing):"; t; " s."
Print
Sleep
Output example:
MEMORY (LONGS) NOT INITIALIZED ON SIZING
----------------------------------------
Memory sizing (sizing then freeing) using 'Allocate'/'Deallocate':
total (sizing + freeing): 0.0001793000041914183 s. <== OK
Memory sizing (sizing then freeing) using 'REDIM+=ANY'/'ERASE ':
total (sizing + freeing): 0.0001632000008129353 s. <== OK
Memory sizing (sizing then freeing) using 'New[]+{ANY}'/'DELETE[]':
total (sizing + freeing): 0.000166699998615627 s. <== OK
=============================================================================
MEMORY (LONGS) INITIALIZED TO ZERO ON SIZING
--------------------------------------------
Memory sizing (sizing then freeing) using 'Callocate'/'Deallocate':
total (sizing + freeing): 0.0001539999972806072 s. <== OK
Memory sizing (sizing then freeing) using 'REDIM'/'ERASE ':
total (sizing + freeing): 0.0001635999952540601 s. <== OK
Memory sizing (sizing then freeing) using 'New[]'/'DELETE[]':
total (sizing + freeing): 0.1539328999944942 s. <== NOK : Non-optimization bug
I compared 'Redim' / 'Erase' with 'New[]' / 'Delete[]' on UDT with or without 'Constructor' / 'Destructor'.
(3 '#define' to activate/deactivate 'Constructor' / 'Destructor' / 'Any' initializer)
Example (compile without code optimization option):
#define withCtor
#define withDtor
#define withAny
#ifdef withCtor
Print "UDT HAS A DEFAULT 'CONSTRUCTOR'"
#else
Print "UDT HAS NO DEFAULT 'CONSTRUCTOR'"
#endif
#ifdef withDtor
Print "UDT HAS A 'DESTRUCTOR'"
#else
Print "UDT HAS NO 'DESTRUCTOR'"
#endif
#ifdef withAny
Print "MEMORY ALLOCATED WITH 'ANY' INITIALIZER"
#else
Print "MEMORY ALLOCATED WITHOUT 'ANY' INITIALIZER"
#endif
Print
Dim As Double t1
Dim As Double t2
Type UDT
Dim As Byte b
#ifdef withCtor
Declare Constructor()
#endif
#ifdef withDtor
Declare Destructor()
#endif
End Type
#ifdef withCtor
Constructor UDT()
End Constructor
#endif
#ifdef withDtor
Destructor UDT()
End Destructor
#endif
#ifdef withAny
Print "Memory sizing (sizing then freeing) using 'REDIM+=ANY'/'ERASE ':"
#else
Print "Memory sizing (sizing then freeing) using 'REDIM'/'ERASE ':"
#endif
t1 = Timer
#ifdef withAny
Redim As UDT u(100000000 - 1) = Any
#else
Redim As UDT u(100000000 - 1)
#endif
t1 = Timer - t1
Print " sizing with Redim:"; t1; " s."
t2 = Timer
Erase u
t2 = Timer - t2
Print " freeing with Erase:"; t2; " s."
Print " total (sizing + freeing):"; t1 + t2; " s."
Print
#ifdef withAny
Print "Memory sizing (sizing then freeing) using 'NEW[]+{ANY}'/'DELETE[]':"
#else
Print "Memory sizing (sizing then freeing) using 'NEW[]'/'DELETE[]':"
#endif
t1 = Timer
#ifdef withAny
Dim As UDT Ptr pu = New UDT[100000000] {Any}
#else
Dim As UDT Ptr pu = New UDT[100000000]
#endif
t1 = Timer - t1
Print " sizing with New[]:"; t1; " s."
t2 = Timer
Delete[](pu)
t2 = Timer - t2
Print " freeing with Delete[]:"; t2; " s."
Print " total (sizing + freeing):"; t1 + t2; " s."
Print
Sleep
Output example for 'Constructor' and 'Destructor' but no 'Any' initializer:
UDT HAS A DEFAULT 'CONSTRUCTOR'
UDT HAS A 'DESTRUCTOR'
MEMORY ALLOCATED WITHOUT 'ANY' INITIALIZER
Memory sizing (sizing then freeing) using 'REDIM'/'ERASE ':
sizing with Redim: 1.646054299994916 s. <== OK
freeing with Erase: 1.406641900001688 s. <== OK
total (sizing + freeing): 3.052696199996603 s. <== OK
Memory sizing (sizing then freeing) using 'NEW[]'/'DELETE[]':
sizing with New[]: 1.677848100002819 s. <== OK
freeing with Delete[]: 1.43740989999359 s. <== OK
total (sizing + freeing): 3.115257999996409 s. <== OK
Output example for no 'Constructor' and no 'Destructor' and no 'Any' initializer:
UDT HAS NO DEFAULT 'CONSTRUCTOR'
UDT HAS NO 'DESTRUCTOR'
MEMORY ALLOCATED WITHOUT 'ANY' INITIALIZER
Memory sizing (sizing then freeing) using 'REDIM'/'ERASE ':
sizing with Redim: 1.680000217874067e-005 s. <== OK
freeing with Erase: 1.359999350825092e-005 s. <== OK
total (sizing + freeing): 3.039999568699159e-005 s. <== OK
Memory sizing (sizing then freeing) using 'NEW[]'/'DELETE[]':
sizing with New[]: 0.02655439999644926 s. <== NOK : Non-optimization bug
freeing with Delete[]: 0.008183699993196569 s. <== nok
total (sizing + freeing): 0.03473809998964583 s. <== NOK : Non-optimization bug
Output example for no 'Constructor' and no 'Destructor' but 'Any' initializer:
UDT HAS NO DEFAULT 'CONSTRUCTOR'
UDT HAS NO 'DESTRUCTOR'
MEMORY ALLOCATED WITH 'ANY' INITIALIZER
Memory sizing (sizing then freeing) using 'REDIM+=ANY'/'ERASE ':
sizing with Redim: 1.819999672392214e-005 s. <== OK
freeing with Erase: 1.589999659046271e-005 s. <== OK
total (sizing + freeing): 3.409999331438485e-005 s. <== OK
Memory sizing (sizing then freeing) using 'NEW[]+{ANY}'/'DELETE[]':
sizing with New[]: 2.109999961419362e-005 s. <== OK
freeing with Delete[]: 1.429999475277555e-005 s. <== OK
total (sizing + freeing): 3.539999436696917e-005 s. <== OK
**To fix the problem of non-optimization in case of no 'Constuctor' and no 'Destructor' and no '{Any}' initializer:
After SARG code inspection, it is confirmed that New / New[] starts by calling:
For large buffers (upwards of several hundred KB), the memory coming from the operating system is already zeroed.
Test code:
Function isMemoryZeroedFromAllocate(Byval size As Uinteger) As Boolean
Dim As Byte Ptr p = Allocate(size)
For I As Uinteger = 0 To size - 1
If p[I] <> 0 Then Deallocate(p) : Return False
Next I
Deallocate(p)
Return True
End Function
Print "'X': memory not zeroed from 'Allocate' (by step of 1 KB allocated)"
Print "'0': memory zeroed from 'Allocate' (by step of 1 KB allocated)"
Print
For I As Integer = 1 * 1024 To 2048 * 1024 Step 1 * 1024
Dim As Boolean b = isMemoryZeroedFromAllocate(I)
If b = True Then Print "0"; Else Print "X";
Next I
Print
Sleep
Output on my PC Windows for 64-bit code:
'X': memory not zeroed from 'Allocate' (by step of 1 KB allocated)
'0': memory zeroed from 'Allocate' (by step of 1 KB allocated)
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
For Windows 64-bit code, memory zeroed from about 1000 KB allocated.
(for Windows 32-bit code, I find a memory zeroed from about 500 KB allocated)
Because 'calloc' is integrated into the memory allocator, it knows if the memory returned comes from the operating system and, if so, it skips the call to 'memset(0)'.
So for such buffers, 'calloc' is much faster than 'malloc' + 'memset(0)'.
Comparison of 1,000,000 zero-initialized 1KB memory allocations, with 10 zero-initialized 100MB memory allocations:
Dim As Double t
Print "1,000,000 zero-initialized 1KB memory allocations:"
Print " Memory sizing (sizing then freeing) using 'Allocate'+'Clear'/'Deallocate':"
t = Timer
For I As Integer = 1 To 1000000
Dim As Byte Ptr p = Allocate(1024)
Clear p[0], 0, 1024
Deallocate(p)
Next I
t = Timer - t
Print " total (sizing + freeing):"; t; " s."
Print " Memory sizing (sizing then freeing) using 'Callocate'/'Deallocate':"
t = Timer
For I As Integer = 1 To 1000000
Dim As Byte Ptr p = Callocate(1024)
Deallocate(p)
Next I
t = Timer - t
Print " total (sizing + freeing):"; t; " s."
Print
Print "10 zero-initialized 100MB memory allocations"
Print " Memory sizing (sizing then freeing) using 'Allocate'+'Clear'/'Deallocate':"
t = Timer
For I As Integer = 1 To 10
Dim As Byte Ptr p = Allocate(100 * 1024 * 1024)
Clear p[0], 0, 100 * 1024 * 1024
Deallocate(p)
Next I
t = Timer - t
Print " total (sizing + freeing):"; t; " s."
Print " Memory sizing (sizing then freeing) using 'Callocate'/'Deallocate':"
t = Timer
For I As Integer = 1 To 10
Dim As Byte Ptr p = Callocate(100 * 1024 * 1024)
Deallocate(p)
Next I
t = Timer - t
Print " total (sizing + freeing):"; t; " s."
Print
Sleep
Output example:
1,000,000 zero-initialized 1KB memory allocations:
Memory sizing (sizing then freeing) using 'Allocate'+'Clear'/'Deallocate':
total (sizing + freeing): 0.09319559999858029 s.
Memory sizing (sizing then freeing) using 'Callocate'/'Deallocate':
total (sizing + freeing): 0.09936769999330863 s.
10 zero-initialized 100MB memory allocations
Memory sizing (sizing then freeing) using 'Allocate'+'Clear'/'Deallocate':
total (sizing + freeing): 0.3598723000031896 s.
Memory sizing (sizing then freeing) using 'Callocate'/'Deallocate':
total (sizing + freeing): 0.0001931999868247658 s.
Fix requested:
For New / New[], replace the sequence 'malloc' + 'memset(0)' by 'calloc'.
Abstract:
See also this other bug report:
#749 For UDT with constructor, NEW does not support ANY as initializer while DIM supports this
Both could be addressed simultaneously.
Last edit: fxm (freebasic.net) 2025-07-30
A fix is proposed by SARG.
**In terms of execution time, I find a very significant performance degradation with '-gen gas64' compared to '-gen gcc' only in the following two cases:
('New' or 'New[]', without 'Any' is OK).**
Can you look specifically at how memory is zeroed in the '-gen gas64' case compared to the '-gen gcc' case, and this applied to both 'placement New' or 'placement New[]', and 'overload New' or 'overload New[]'.
(Performance difference between the two instructions types to zero the memory?)
Test example (the three time values should be about the same):
Note: This performance difference has existed since the creation of gas64 (it is not a recent degradation because it exists since fbc version 1.08).
See also from this forum post and the followings:
https://www.freebasic.net/forum/viewtopic.php?p=308738#p308738
A fix is proposed by SARG.