Am 31.12.2023 um 04:11 schrieb Amir--- via fpc-pascal:
On 12/30/23 00:20, Sven Barth via fpc-pascal wrote:
Amir via fpc-pascal <fpc-pascal@lists.freepascal.org> schrieb am Sa.,
30. Dez. 2023, 08:11:
On Dec 29, 2023 9:50 PM, Adriaan van Os <adri...@adriaan.biz> 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.
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
_______________________________________________
fpc-pascal maillist - fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal