I was comparing performances between these two ways of initializing a dynamic array:
Arr := TArray<integer>.Create(1, 2, 3, 4, 5);
and
SetLength(Arr, 5);
Arr[0] := 1;
Arr[1] := 2;
Arr[2] := 3;
Arr[3] := 4;
Arr[4] := 5;
I've prepared a test and I've noticed that using the array "constructor" takes twice the time required by the other method.
Test:
uses
DateUtils;
function CreateUsingSetLength() : TArray<integer>;
begin
SetLength(Result, 5);
Result[0] := 1;
Result[1] := 2;
Result[2] := 3;
Result[3] := 4;
Result[4] := 5;
end;
...
const
C_COUNT = 10000000;
var
Start : TDateTime;
i : integer;
Arr : TArray<integer>;
MS1 : integer;
MS2 : integer;
begin
Start := Now;
i := 0;
while(i < C_COUNT) do
begin
Arr := TArray<integer>.Create(1, 2, 3, 4, 5);
Inc(i);
end;
MS1 := MillisecondsBetween(Now, Start);
Start := Now;
i := 0;
while(i < C_COUNT) do
begin
Arr := CreateUsingSetLength();
Inc(i);
end;
MS2 := MillisecondsBetween(Now, Start);
ShowMessage('Constructor = ' + IntToStr(MS1) + sLineBreak + 'Other method = ' + IntToStr(MS2));
Testing on my machine, the resulting values are always near the following:
Constructor = 622
Other method = 288
Why is the array "constructor" so slow?
Let's take a look at the code that was generated (optimization on, Win32 target, 10.2 Tokyo):
Project152.dpr.34: Arr := TArray<Integer>.Create(1, 2, 3, 4, 5);
004D0D22 8D45F8 lea eax,[ebp-$08]
004D0D25 8B15B84B4000 mov edx,[$00404bb8]
004D0D2B E858BFF3FF call @DynArrayClear
004D0D30 6A05 push $05
004D0D32 8D45F8 lea eax,[ebp-$08]
004D0D35 B901000000 mov ecx,$00000001
004D0D3A 8B15B84B4000 mov edx,[$00404bb8]
004D0D40 E81FBEF3FF call @DynArraySetLength
004D0D45 83C404 add esp,$04
004D0D48 8B45F8 mov eax,[ebp-$08]
004D0D4B C70001000000 mov [eax],$00000001
004D0D51 8B45F8 mov eax,[ebp-$08]
004D0D54 C7400402000000 mov [eax+$04],$00000002
004D0D5B 8B45F8 mov eax,[ebp-$08]
004D0D5E C7400803000000 mov [eax+$08],$00000003
004D0D65 8B45F8 mov eax,[ebp-$08]
004D0D68 C7400C04000000 mov [eax+$0c],$00000004
004D0D6F 8B45F8 mov eax,[ebp-$08]
004D0D72 C7401005000000 mov [eax+$10],$00000005
004D0D79 8B55F8 mov edx,[ebp-$08]
004D0D7C 8D45FC lea eax,[ebp-$04]
004D0D7F 8B0DB84B4000 mov ecx,[$00404bb8]
004D0D85 E842BFF3FF call @DynArrayAsg
and:
Project152.dpr.12: SetLength(Result, 5);
004D0CB2 6A05 push $05
004D0CB4 8BC3 mov eax,ebx
004D0CB6 B901000000 mov ecx,$00000001
004D0CBB 8B15B84B4000 mov edx,[$00404bb8]
004D0CC1 E89EBEF3FF call @DynArraySetLength
004D0CC6 83C404 add esp,$04
Project152.dpr.13: Result[0] := 1;
004D0CC9 8B03 mov eax,[ebx]
004D0CCB C70001000000 mov [eax],$00000001
Project152.dpr.14: Result[1] := 2;
004D0CD1 8B03 mov eax,[ebx]
004D0CD3 C7400402000000 mov [eax+$04],$00000002
Project152.dpr.15: Result[2] := 3;
004D0CDA 8B03 mov eax,[ebx]
004D0CDC C7400803000000 mov [eax+$08],$00000003
Project152.dpr.16: Result[3] := 4;
004D0CE3 8B03 mov eax,[ebx]
004D0CE5 C7400C04000000 mov [eax+$0c],$00000004
Project152.dpr.17: Result[4] := 5;
004D0CEC 8B03 mov eax,[ebx]
004D0CEE C7401005000000 mov [eax+$10],$00000005
So it is clear that the code generated for the "constructor" call is simply less optimized.
As you can see, the "constructor" code first clears, allocates and fills an anonymous array (at [ebp-$08]
) and at the end, assigns that to the Arr
variable (at [ebp-$04]
). That is mainly why it is slower.
In newer versions, there is a third way:
Arr := [1, 2, 3, 4, 5];
But this produces the exact same code as the "constructor" syntax. But you can speed this up with:
const
C_ARR = [1, 2, 3, 4, 5]; // yes, dynarray const!
and
Arr := C_ARR;
This simply generates the dynamic array once, with a reference count of -1, and in the loop, simply does an assignment (well, in _DynArrayAsg
, actually a copy -- but this is still faster):
Project152.dpr.63: Arr := C_ARR;
004D0E60 8D45FC lea eax,[ebp-$04]
004D0E63 8B15C4864D00 mov edx,[$004d86c4]
004D0E69 8B0DB84B4000 mov ecx,[$00404bb8]
004D0E6F E858BEF3FF call @DynArrayAsg
But, as @DavidHeffernan commented, in real life programming, these performance differences will hardly ever be noticed. You generally don't initialize such arrays in tight loops, and in a one-off situation, the difference is a few nanoseconds, which you won't notice during the entire run of the program.
There seems to be some confusion. The type TArray<Integer>
is exactly the same as an array of Integer
. Neither are classes or some other kind of wrappers for dynamic arrays. They are plain dynamic arrays and nothing else. The constructor syntax can be applied to both. The only difference is in type compatibility. TArray<Integer>
can be used as an ad-hoc type declaration, and all TArray<Integer>
are type-compatible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With