The dynamic arrays in Lazarus have so many terrific properties. Giving power
users the ability to not initialize them would give clear performance benefits.
This is useful in real world sitautions where one has long term arrays, but
also can use transient arrays of the same type. Another feature that could help
power users is the ability to set the alignment of setlength() - see issue
0034031. From my view, dynamic arrays are one of the features that makes Pascal
code so much nicer than C. Web searches for "Speed "problem" with SetLength”
and "Faster way of initializing arrays in Delphi” suggest others encounter
these issues as well. I would advocate for a couple of advanced and optional
features (alignment and initialization control) would allow this elegant
property of Pascal to scale into these niches. It certainly makes code moree
readable and consistent to keep one type of structure, rather than having to
switch to GetMem for doing some tasks (SIMD, GPU, transient large arrays).
Giving users control can obviously deviate from guaranteed behavior (e.g. one
could choose a poor alignment, or not initialize strong arrays and leave random
data in elements). However, allowing advanced users to elect to use these
features would really help dynamic arrays scale to problems we face in the real
world.
Despite these preferences, the experiment below shows that even for very large
arrays, the initialization penalty may not be big so long as long you fill the
array immediately after setting the length of the array. Note that SetLength()
is very slow, but the subsequent filling of the array is fast (presumably due
to cache), while getmem returns quickly, but the intiailization is
substantially slower. I would be grateful if anyone can suggest any issues here
or any method to accelerate either set of routines.
Milliseconds SetLength: 214..221 mean 218
Milliseconds fill array: 56..103 mean 95
Milliseconds GetMem: 0..0 mean 0
Milliseconds fill array: 251..270 mean 259
--------------------
program test3;
//fpc -O3 test3.pas; ./test3
{$IFDEF FPC}{$mode delphi} {$H+}{$ENDIF}
uses Math, SysUtils,DateUtils;
type
TUInt32s = array of uint32;
TInt32s = array of int32;
TRGBA = packed record //red,green,blue,alpha
R,G,B,A : byte;
end;
TRGBAs = array of TRGBA;
TRGBA0 = array [0..MAXINT] of TRGBA;
TRGBAp = ^TRGBA0; //pointer to RGBA array
TUInt320 = array [0..MAXINT] of uint32;
TUInt32p = ^TUInt320; //pointer to RGBA array
procedure SetLengthP(var S: TRGBAp; Len: SizeInt); overload;
begin
ReAllocMem(S, Len *sizeof(TRGBA));
end;
procedure setlengthTest();
const
nVox = (512 * 512 * 512); //typical CT scan as RGBA array
nTest = 10;
var
startTime: TDateTime;
i, j, ms, mn,mx,tot, mn2, mx2, tot2: int64;
ArrayDyn: TRGBAs;
Arrayp: TRGBAp;
asUint32s: TUInt32s;
asUint32p: TUInt32p;
begin
//setlength() is slow, presumably because it intializes array (fillchar())
//https://alt.comp.lang.borland-delphi.narkive.com/O0jRrNgS/speed-problem-with-setlength
//test setlength
mn := maxint;
mx := 0;
tot := 0;
mn2 := maxint;
mx2 := 0;
tot2 := 0;
for i := 1 to nTest do begin
startTime := Now;
SetLength(ArrayDyn, nVox);
ms := MilliSecondsBetween(Now,startTime);
mn := min(ms,mn);
mx := max(ms,mx);
tot += ms;
//fill arrays
startTime := Now;
asUint32s := TUInt32s(ArrayDyn);
for j := 0 to nVox-1 do
asUint32s[j] := (j );
SetLength(ArrayDyn,0);
ms := MilliSecondsBetween(Now,startTime);
mn2 := min(ms,mn2);
mx2 := max(ms,mx2);
tot2 += ms;
end;
writeln(format('Milliseconds SetLength: %d..%d mean %d', [mn,mx,
round(tot/nTest)]));
writeln(format('Milliseconds fill array: %d..%d mean %d', [mn2,mx2,
round(tot2/nTest)]));
//test GetMem
mn := maxint;
mx := 0;
tot := 0;
mn2 := maxint;
mx2 := 0;
tot2 := 0;
Arrayp := nil; //must initialize!
for i := 1 to nTest do begin
startTime := Now;
SetLengthP(Arrayp, nVox);
ms := MilliSecondsBetween(Now,startTime);
mn := min(ms,mn);
mx := max(ms,mx);
tot += ms;
//fill arrays
startTime := Now;
{$IFDEF X}
asUint32s := TUInt32s(Arrayp);
for j := 0 to nVox-1 do
asUint32s[j] := (j );
{$ELSE}
asUint32p := TUInt32p(Arrayp);
for j := 0 to nVox-1 do
asUint32p[j] := (j );
{$ENDIF}
SetLengthP(Arrayp,0);
ms := MilliSecondsBetween(Now,startTime);
mn2 := min(ms,mn2);
mx2 := max(ms,mx2);
tot2 += ms;
end;
writeln(format('Milliseconds GetMem: %d..%d mean %d', [mn,mx,
round(tot/nTest)]));
writeln(format('Milliseconds fill array: %d..%d mean %d', [mn2,mx2,
round(tot2/nTest)]));
end;
begin
setlengthTest();
Exit;
end.
_______________________________________________
fpc-devel maillist - [email protected]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel