I really do not think this is a fair comparison.@Xor-el Explain why I can't compare TVector and TList ?
You need to compare fgl's TFPGList with generics.collections not gvector's TVector with generics.collections.
This benchmark has not much sense. TList<T> has notifications and virtual methods, meanwhile TVector<T> doesn't have any virtual methods nor notifications but has many inline methods (it is "just" wrapper around dynamic array). The purpose of both collections is different.what is the the purpose of TList ? When use TList and when use TVector ?
I am sure that you should add raw dynamic array to this benchmark. I am sure that dynamic array will be the winner.
Basics:No. Can you give practical example ?
A list is not a vector.
A list is for storage and retrieve. And optimized for that.
A vector is intended for calculation. And optimized for that.
Does that explain it for you?
Here is my simple bench - appending LongInt to the end. TVector is winnerThanks. Conclusion - if do not need additional features of TList<T> and speed is important, use TVector<T>.
@Xor-el Explain why I can't compare TVector and TList ?
I know that TVector is C++ std::vector analog. What is analog of TList in C++ or any other language ?A list? Basics. In this case it is a doubly linked list internally, so it has superior search, sort and insert/delete as opposed to a vector on unsorted data.
In this case it is a doubly linked list internallySo Generics.Collections TList is doubly linked list ?
@julkas, I just tried to exclude from the benchmark the time spent on data generation:@avk Good. But what makes so big diff ? My bench uses same logic for loop for both classes ...
program fclvsgc; {$mode delphi} uses SysUtils, Classes, gvector, Generics.Defaults, Generics.Collections; type TVect = TVector<LongInt>; TData = array of Integer; var scv: TVect; scl: Generics.Collections.TList<LongInt>; i: LongInt; cnt: LongInt; start: QWord; function GenData(s: LongInt): TData; var I: Integer; begin SetLength(Result, s); for I := 0 to High(Result) do Result[I] := Random(2147483647); end; procedure PopulateVector(s: LongInt); var Data: TData; begin Data := GenData(s); scv := TVect.Create; start := GetTickCount64(); for i in Data do scv.PushBack(i); WriteLn('Vector populated - ', s, ' keys in ', GetTickCount64() - start, ' ticks.'); FreeAndNil(scv); end; procedure PopulateList(s: LongInt); var Data: TData; begin Data := GenData(s); scl := Generics.Collections.TList<LongInt>.Create; start := GetTickCount64(); for i in Data do scl.Add(i); WriteLn('List populated - ', s, ' keys in ', GetTickCount64() - start, ' ticks.'); FreeAndNil(scl); end; begin cnt := 100000; PopulateVector(cnt); PopulateList(cnt); cnt := 10*cnt; PopulateVector(cnt); PopulateList(cnt); cnt := 10*cnt; PopulateVector(cnt); PopulateList(cnt); cnt := 10*cnt; PopulateVector(cnt); PopulateList(cnt); cnt := 5*cnt; PopulateVector(cnt); PopulateList(cnt); WriteLn('End.'); end.
And I can’t confirm your conclusion, the results show another(FPC 3.3.1):
Vector populated - 100000 keys in 0 ticks. List populated - 100000 keys in 0 ticks. Vector populated - 1000000 keys in 0 ticks. List populated - 1000000 keys in 0 ticks. Vector populated - 10000000 keys in 172 ticks. List populated - 10000000 keys in 125 ticks. Vector populated - 100000000 keys in 2044 ticks. List populated - 100000000 keys in 1294 ticks. Vector populated - 500000000 keys in 15163 ticks. List populated - 500000000 keys in 6505 ticks. End.
@julkas, can you show body of your(3.0.4) version of gvector.TVector.IncreaseCapacity?All my tests done on Lazarus 2.0.0 / FPC 3.0.4 and Generics.Collections cloned from https://github.com/maciej-izak/generics.collections
Mine(3.3.1) looks like this:
procedure TVector.IncreaseCapacity(); const // if size is small, multiply by 2; // if size bigger but <256M, inc by 1/8*size; // otherwise inc by 1/16*size cSizeSmall = 1*1024*1024; cSizeBig = 256*1024*1024; var DataSize:SizeUInt; begin DataSize:=FCapacity*SizeOf(T); if FCapacity=0 then FCapacity:=4 else if DataSize<cSizeSmall then FCapacity:=FCapacity*2 else if DataSize<cSizeBig then FCapacity:=FCapacity+FCapacity div 8 else FCapacity:=FCapacity+FCapacity div 16; SetLength(FData, FCapacity); end;
procedure PopulateList(s: LongInt);
var
Data: TData;
begin
Data := GenData(s);
scl := Generics.Collections.TList<LongInt>.Create;
start := GetTickCount64();
for i in Data do
scl.Add(i);
WriteLn('List populated - ', s, ' keys in ', GetTickCount64() - start, ' ticks.');
FreeAndNil(scl);
end;
@ k1ng, thanks, but I'm just trying to simulate the case when the number of added items is not known in advance.@avk Why TData = array of Integer; ?
@julkas, well, that’s the reason for such a big difference. Someone has already taken care to fix the mess you discovered. :-X
As you know the number of items to be added, you can doTest with this behavior (include simple array of)That might give another performance boost.
scl.Capacity := s;
{$MODE OBJFPC}
@ASerge, if the number of added elements is known in advance, then there’s nothing to talk about.I commented out lines 41, 56 and 83 in my source, and result is:
Your code is a complete different mode, so I guess it's not comparable at all with the previous posted codes.Due to the fact that RTL is compiled in OBJFPC mode, it is more logical to test it in the same mode.
@ASerge,FPC 3.3.1 rev 42572.suspect you're using 3.0.4?So this is the point.
But the initial post uses $mode Delphi, so I'm sure he did that because he needs Delphi compatibility as most people do.Your code is a complete different mode, so I guess it's not comparable at all with the previous posted codes.Due to the fact that RTL is compiled in OBJFPC mode, it is more logical to test it in the same mode.
@k1ng +1.But the initial post uses $mode Delphi, so I'm sure he did that because he needs Delphi compatibility as most people do.Your code is a complete different mode, so I guess it's not comparable at all with the previous posted codes.Due to the fact that RTL is compiled in OBJFPC mode, it is more logical to test it in the same mode.
Delphi 10.3.2 - 32bitDelphi 10.3.2 - 64bit
List populated - 100000 keys in 0 ticks. List populated - 1000000 keys in 0 ticks. List populated - 10000000 keys in 47 ticks. List populated - 100000000 keys in 438 ticks. -- out of memory for last combo
List populated - 100000 keys in 0 ticks. List populated - 1000000 keys in 0 ticks. List populated - 10000000 keys in 47 ticks. List populated - 100000000 keys in 453 ticks. List populated - 500000000 keys in 2328 ticks.
ported code to work in Delphi:
program fclvsgc; {$apptype CONSOLE} uses SysUtils, Classes, windows, Generics.Defaults, Generics.Collections; type TData = array of Integer; var scl: Generics.Collections.TList<LongInt>; i: LongInt; cnt: LongInt; start: UInt64; function GenData(s: LongInt): TData; var I: Integer; begin SetLength(Result, s); for I := 0 to High(Result) do Result[I] := Random(2147483647); end; procedure PopulateList(s: LongInt); var Data: TData; begin Data := GenData(s); scl := Generics.Collections.TList<LongInt>.Create; scl.Capacity := s; start := GetTickCount64(); for i in Data do scl.Add(i); WriteLn('List populated - ', s, ' keys in ', GetTickCount64() - start, ' ticks.'); FreeAndNil(scl); end; begin cnt := 100000; PopulateList(cnt); cnt := 10*cnt; PopulateList(cnt); cnt := 10*cnt; PopulateList(cnt); cnt := 10*cnt; PopulateList(cnt); cnt := 5*cnt; PopulateList(cnt); WriteLn('End.'); readln; end.
...FPC 3.3.1 rev 42572...@ASerge, thanks, I see this.
Any Delphi (Community) vs FP Generics.Collections bench, test?This code:
That should only matter for parsing not for the generated code (with very small exceptions regarding type conversions and such).Your code is a complete different mode, so I guess it's not comparable at all with the previous posted codes.Due to the fact that RTL is compiled in OBJFPC mode, it is more logical to test it in the same mode.