Amir--- wrote 111 lines
On 12/30/23 00:20, Sven Barth via fpc-pascal wrote:
> Amir via fpc-pascal schrieb am Sa.,
> 30. Dez. 2023, 08:11:
>
>
>
> On Dec 29, 2023 9:50 PM, Adriaan van Os wrote:
>
> Amir--- via fpc-pascal wrote:
> > Hi all,
> >
> > I have a List of record, where the record has a WideString
> field.
> > I have some code like the following:
> >
> > function check(constref v: TMyRecord; data:
> TListOfMyRecord): Boolean;
> > var
> > r: TMyRecord;
> >
> > begin
> > Result := False;
> > for r in data do
> > if r.State = v.State then
> > Exit(True);
> > end;
> >
> > I call this method a lot and the CPU profiling shows a lot
> of cpu time
> > spent on "fpc_copy_proc" (which I assume is doing the deep
> copy on
> > records) from "TCustomListEnumerator.GetCurrent".
> > I considered other alternatives like using enumerators but
> they all need
> > a to return a record (and hence copying the widestring field).
> > I can think of two solutions to get rid of the wasting(!)
> so much time
> > on "fpc_copy_proc":
> > 1) Changing the TMyRecord to TMyClass. But then I need to
> Create and
> > Free a "lot" of objects.
> > 2) Update TListOfMyRecord to TListOfPointerToMyRecord. This
> requires a
> > "lot" of memory allocation/fragmentation.
> >
> > Is there a better solution?
>
> Pass the data parameter by reference.
>
> This means I need to have a ListOfMyRecord and a
> ListOfConstRefMyRecord, right?
>
>
> No, that's not a thing.
>
> You simply need to declare your "data" parameter as "constref" or
> "const" as well, just like your "v" parameter.
Have a look at this piece of code (The complete code is attached):
type
TMyRecord = record
Str: AnsiString;
Index: Integer;
end;
PMyRecord = ^TMyRecord;
TMyList = specialize TList<TMyRecord>;
TMyPtrList = specialize TList<PMyRecord>;
function Check1(const MyList: TMyList): Integer;
var
data: TMyRecord;
begin
Result := 0;
for data in MyList do
if data.Index mod 100 = 0 then
Inc(Result);
end;
function Check2(MyList: TMyList): Integer;
var
data: TMyRecord;
begin
Result := 0;
for data in MyList do
if data.Index mod 100 = 0 then
Inc(Result);
end;
function Check3(MyPtrList: TMyPtrList): Integer;
var
data: PMyRecord;
begin
Result := 0;
for data in MyPtrList do
if data^.Index mod 100 = 0 then
Inc(Result);
end;
I compiled the code with `fpc -O3 -Sd -gv -g -gl ` and ran `valgrind`
on it (the output is attached). It does not look like there is a big
difference between the Check1 and Check2 but Check3 is about 20 times
faster than the other two.
For a class type there isn't much difference between being passed as "const" or not. It's mainly records and managed types this affects.
Show quoted text 12 lines
I believe the issue could be resolved if we make
"TCustomListWithPointers.GetPtrEnumerator" a public method. Then, one
can implement the following function:
function Check4(MyList: TMyList): Integer;
....
it := MyList.GetPreEnumerator;
while it.MoveNext do
begin
if it.Current^.Index mod 100 = 0 then
....
end;
You simply need to inherit from the list class so that you can make the function public. And with a little trick you can also use it inside a for-in-loop:
=== code begin ===
program tlistitr;
{$mode objfpc}{$H+}
{$modeswitch advancedrecords}
uses
Generics.Collections;
type
TRec = record
a, b, c: Int64;
constructor Create(aArg1, aArg2, aArg3: Int64);
end;
{ this way the GetPtrEnumerator function is available; you could also use a class helper }
TMyList = class(specialize TList<TRec>)
public
function GetPtrEnumerator: specialize TEnumerator<PT>; end;
constructor TRec.Create(aArg1, aArg2, aArg3: Int64);
begin
a := aArg1;
b := aArg2;
c := aArg3;
end;
function TMyList.GetPtrEnumerator: specialize TEnumerator<PT>;
begin
Result := inherited GetPtrEnumerator();
end;
{ with this you can simply do "for PtrTypeVar in List.GetPtrEnumerator do",
though this *might* not work in mode Delphi... }
operator Enumerator(aEnum: specialize TEnumerator<TMyList.PT>):
specialize TEnumerator<TMyList.PT>;
begin
Result := aEnum;
end;
var
l: TMyList;
r: TRec;
p: TMyList.PT;
begin
l := TMyList.Create;
l.Add(TRec.Create(1, 2, 3));
l.Add(TRec.Create(9, 8, 7));
for p in l.GetPtrEnumerator do begin
Writeln(p^.a, ' ', p^.b, ' ', p^.c);
end;
end.
=== code end ===
Regards,
Sven