Delphi and Free Pascal bencmark: array vs TList vs dynamic array
Let's perform a simple benchmark testing the random and sequential access to arrays and lists. I would like to compare:
- static array with predefined size (allocated on stack)
TList
container class (allocated on heap)- dynamic array (allocated on heap)
- generic Delphi
TList
or Free PascalTFPGList
(allocated on heap)
I use an array/list of 10M elements of integer
and varaint
data type. You can change the type definition in "type" section if need to test other cases.
program Bench1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
SysUtils,
Classes,
DateUtils,
{$IFDEF FPC}FGL{$ELSE}System.Generics.Collections{$ENDIF},
Math;
const
MaxDimension = 10000000;
type
TTestedValueType = integer;
PTestedValueType = ^TTestedValueType;
TTestGenericList = {$IFDEF FPC}specialize TFPGList<PTestedValueType>{$ELSE}TList<PTestedValueType>{$ENDIF};
var
D1, D2: TDateTime;
RndAddr: array [0..MaxDimension - 1] of integer;
TestArray: array [0..MaxDimension - 1] of TTestedValueType;
TestList: TList;
TestGenericList: TTestGenericList;
TestDynArray: array of TTestedValueType;
p: PTestedValueType;
i: integer;
Sum: double;
begin
Randomize;
writeln('Prepare random access array');
for i := 0 to MaxDimension - 1 do
RndAddr[i] := Math.RandomRange(0, MaxDimension);
// Static array
for i := 0 to MaxDimension - 1 do
TestArray[i] := 0;
D1 := Now;
for i := 0 to MaxDimension - 1 do
TestArray[RndAddr[i]] := RndAddr[i];
D2 := Now;
writeln('Static array. Random write access. Elapsed, sec: ', DateUtils.SecondSpan(D2, D1):5:2);
Sum := 0;
D1 := Now;
for i := 0 to MaxDimension - 1 do
Sum := Sum + TestArray[i];
D2 := Now;
writeln('Static array. Sequential read access. Elapsed, sec: ',
DateUtils.SecondSpan(D2, D1):5:2,
'. Sum: ', Sum);
// TList
writeln('Prepare list');
TestList := TList.Create;
TestList.Capacity := MaxDimension;
for i := 0 to MaxDimension - 1 do
begin
new(p);
p^ := 0;
TestList.Add(p);
end;
D1 := Now;
for i := 0 to MaxDimension - 1 do
PTestedValueType(TestList[RndAddr[i]])^ := RndAddr[i];
D2 := Now;
writeln('TList. Random write access. Elapsed, sec: ', DateUtils.SecondSpan(D2, D1):5:2);
Sum := 0;
D1 := Now;
for i := 0 to MaxDimension - 1 do
Sum := Sum + PTestedValueType(TestList[i])^;
D2 := Now;
writeln('TList. Sequential access. Elapsed, sec: ',
DateUtils.SecondSpan(D2, D1):5:2,
'. Sum: ', Sum);
for i := 0 to MaxDimension - 1 do
dispose(PTestedValueType(TestList[i]));
TestList.Free;
// Dynamic array
writeln('Prepare dynamic array');
SetLength(TestDynArray, MaxDimension);
for i := 0 to MaxDimension - 1 do
TestDynArray[i] := 0;
D1 := Now;
for i := 0 to MaxDimension - 1 do
TestDynArray[RndAddr[i]] := RndAddr[i];
D2 := Now;
writeln('Dynamic array. Random write access. Elapsed, sec: ', DateUtils.SecondSpan(D2, D1):5:2);
D1 := Now;
Sum := 0;
for i := 0 to MaxDimension - 1 do
Sum := Sum + TestDynArray[i];
D2 := Now;
writeln('Dynamic array. Sequential access. Elapsed, sec: ',
DateUtils.SecondSpan(D2, D1):5:2,
'. Sum: ', Sum);
SetLength(TestDynArray, 0);
// Generic list
writeln('Prepare generic list');
TestGenericList := TTestGenericList.Create;
TestGenericList.Capacity := MaxDimension;
for i := 0 to MaxDimension - 1 do
begin
new(p);
p^ := 0;
TestGenericList.Add(p);
end;
D1 := Now;
for i := 0 to MaxDimension - 1 do
PTestedValueType(TestGenericList[RndAddr[i]])^ := RndAddr[i];
D2 := Now;
writeln('TTestGenericList. Random write access. Elapsed, sec: ', DateUtils.SecondSpan(D2, D1):5:2);
Sum := 0;
D1 := Now;
for i := 0 to MaxDimension - 1 do
Sum := Sum + PTestedValueType(TestGenericList[i])^;
D2 := Now;
writeln('TTestGenericList. Sequential read access. Elapsed, sec: ',
DateUtils.SecondSpan(D2, D1):5:2,
'. Sum: ', Sum);
for i := 0 to MaxDimension - 1 do
dispose(PTestedValueType(TestGenericList[i]));
TestGenericList.Free;
end.
Results
For integer data type both static and dynamic arrays are faster than the lists. Now change value type to variant.
type
TTestedValueType = variant;
After restarting the test, you can observe the same sample.
Integer | Variant | |||
---|---|---|---|---|
Random write access, sec | Sequential read access, sec | Random write access, sec | Sequential read access, sec | |
Free Pascal | ||||
Static array | 0.20 | 0.04 | 0.77 | 1.02 |
TList | 0.96 | 0.08 | 1.74 | 1.06 |
Dynamic array | 0.20 | 0.04 | 0.74 | 1.06 |
TTestGenericList | 1.37 | 0.10 | 1.87 | 1.23 |
Delphi | ||||
Static array | 0.29 | 0.04 | 0.63 | 0.52 |
TList | 2.29 | 0.05 | 3.59 | 0.54 |
Dynamic array | 0.29 | 0.04 | 0.69 | 0.53 |
TTestGenericList | 2.31 | 0.05 | 3.48 | 0.57 |
Conclusions
For different value types like integer or variant the static or dynamic array are the fastest solutions in Delphi and FPC.
Remember, that "fastest" doesn't mean "better".
Test environment
- HP ProBook 6550b, Intel(R) Core(TM) i5 CPU M 520, RAM 8 GB
- Free Pascal case:
- Ubuntu 14.04 64 bits Linux 3.16.0-60-generic
- Free Pascal Compiler version 3.0.0 [2015/12/05] for x86_64
- O1 optimization level
- Delphi case:
- Windows 10 64 bits
- Delphi 10.2 Tokyo
- Optimization is on
blog comments powered by Disqus