<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<div class="moz-cite-prefix">Am 31.12.2023 um 04:11 schrieb Amir---
via fpc-pascal:<br>
</div>
<blockquote type="cite"
cite="mid:01d55559-93a3-449a-b33b-867a95900988@Aavani.net">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<br>
<div class="moz-cite-prefix">On 12/30/23 00:20, Sven Barth via
fpc-pascal wrote:<br>
</div>
<blockquote type="cite"
cite="mid:CAFMUeB8OJMJ8oBcx_62_pwTBiq8sjJMCFXArA0AEAsQhf7DxAA@mail.gmail.com">
<meta http-equiv="content-type"
content="text/html; charset=UTF-8">
<div dir="auto">
<div>
<div class="gmail_quote">
<div dir="ltr" class="gmail_attr">Amir via fpc-pascal <<a
href="mailto:fpc-pascal@lists.freepascal.org"
moz-do-not-send="true" class="moz-txt-link-freetext">fpc-pascal@lists.freepascal.org</a>>
schrieb am Sa., 30. Dez. 2023, 08:11:<br>
</div>
<blockquote class="gmail_quote"
style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div dir="auto">
<div><br>
<div class="gmail_extra"><br>
<div class="gmail_quote">On Dec 29, 2023 9:50 PM,
Adriaan van Os <<a
href="mailto:adriaan@adriaan.biz"
target="_blank" rel="noreferrer"
moz-do-not-send="true"
class="moz-txt-link-freetext">adriaan@adriaan.biz</a>>
wrote:<br type="attribution">
<blockquote
style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Amir---
via fpc-pascal wrote: <br>
> Hi all, <br>
> <br>
> I have a List of record, where the
record has a WideString field. <br>
> I have some code like the following: <br>
> <br>
> function check(constref v: TMyRecord;
data: TListOfMyRecord): Boolean; <br>
> var <br>
> r: TMyRecord; <br>
> <br>
> begin <br>
> Result := False; <br>
> for r in data do <br>
> if r.State = v.State then <br>
> Exit(True); <br>
> end; <br>
> <br>
> I call this method a lot and the CPU
profiling shows a lot of cpu time <br>
> spent on "fpc_copy_proc" (which I assume
is doing the deep copy on <br>
> records) from
"TCustomListEnumerator.GetCurrent". <br>
> I considered other alternatives like
using enumerators but they all need <br>
> a to return a record (and hence copying
the widestring field). <br>
> I can think of two solutions to get rid
of the wasting(!) so much time <br>
> on "fpc_copy_proc": <br>
> 1) Changing the TMyRecord to TMyClass.
But then I need to Create and <br>
> Free a "lot" of objects. <br>
> 2) Update TListOfMyRecord to
TListOfPointerToMyRecord. This requires a <br>
> "lot" of memory allocation/fragmentation.
<br>
> <br>
> Is there a better solution? <br>
<br>
Pass the data parameter by reference.</blockquote>
</div>
</div>
</div>
<div dir="auto">This means I need to have a
ListOfMyRecord and a ListOfConstRefMyRecord, right? </div>
</div>
</blockquote>
</div>
</div>
<div dir="auto"><br>
</div>
<div dir="auto">No, that's not a thing.</div>
<div dir="auto"><br>
</div>
<div dir="auto">You simply need to declare your "data"
parameter as "constref" or "const" as well, just like your
"v" parameter. </div>
</div>
</blockquote>
Have a look at this piece of code (The complete code is attached):<br>
<font face="monospace">type</font><br>
<font face="monospace"> TMyRecord = record</font><br>
<font face="monospace"> Str: AnsiString;</font><br>
<font face="monospace"> Index: Integer;</font><br>
<br>
<font face="monospace"> end;</font><br>
<font face="monospace"> PMyRecord = ^TMyRecord;</font><br>
<br>
<font face="monospace"> TMyList = specialize
TList<TMyRecord>;</font><br>
<font face="monospace"> TMyPtrList = specialize
TList<PMyRecord>;</font><br>
<br>
<font face="monospace">function Check1(const MyList: TMyList):
Integer;</font><br>
<font face="monospace">var</font><br>
<font face="monospace"> data: TMyRecord;</font><br>
<br>
<font face="monospace">begin</font><br>
<font face="monospace"> Result := 0;</font><br>
<font face="monospace"> for data in MyList do</font><br>
<font face="monospace"> if data.Index mod 100 = 0 then</font><br>
<font face="monospace"> Inc(Result);</font><br>
<br>
<font face="monospace">end;</font><br>
<br>
<font face="monospace">function Check2(MyList: TMyList): Integer;</font><br>
<font face="monospace">var</font><br>
<font face="monospace"> data: TMyRecord;</font><br>
<br>
<font face="monospace">begin</font><br>
<font face="monospace"> Result := 0;</font><br>
<font face="monospace"> for data in MyList do</font><br>
<font face="monospace"> if data.Index mod 100 = 0 then</font><br>
<font face="monospace"> Inc(Result);</font><br>
<br>
<font face="monospace">end;</font><br>
<br>
<font face="monospace">function Check3(MyPtrList: TMyPtrList):
Integer;</font><br>
<font face="monospace">var</font><br>
<font face="monospace"> data: PMyRecord;</font><br>
<br>
<font face="monospace">begin</font><br>
<font face="monospace"> Result := 0;</font><br>
<font face="monospace"> for data in MyPtrList do</font><br>
<font face="monospace"> if data^.Index mod 100 = 0 then</font><br>
<font face="monospace"> Inc(Result);</font><br>
<br>
<font face="monospace">end;</font><br>
<br>
<br>
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.<br>
</blockquote>
<br>
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.<br>
<br>
<blockquote type="cite"
cite="mid:01d55559-93a3-449a-b33b-867a95900988@Aavani.net"> I
believe the issue could be resolved if we make
"TCustomListWithPointers.GetPtrEnumerator" a public method. Then,
one can implement the following function:<br>
<br>
<font face="monospace">function Check4(MyList: TMyList): Integer;</font><br>
<font face="monospace">....</font><br>
<font face="monospace"> it := MyList.GetPreEnumerator;</font><br>
<font face="monospace"> while it.MoveNext do</font><br>
<font face="monospace"> begin</font><br>
<font face="monospace"> if it.Current^.Index mod 100 = 0 then</font><br>
<font face="monospace"> ....</font><br>
<font face="monospace"> end;</font><br>
</blockquote>
<br>
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:<br>
<br>
=== code begin ===<br>
<br>
program tlistitr;<br>
<br>
{$mode objfpc}{$H+}<br>
{$modeswitch advancedrecords}<br>
<br>
uses<br>
Generics.Collections;<br>
<br>
type<br>
TRec = record<br>
a, b, c: Int64;<br>
constructor Create(aArg1, aArg2, aArg3: Int64);<br>
end;<br>
<br>
{ this way the GetPtrEnumerator function is available; you could
also use a class helper }<br>
TMyList = class(specialize TList<TRec>)<br>
public<br>
function GetPtrEnumerator: specialize TEnumerator<PT>;<br>
end;<br>
<br>
constructor TRec.Create(aArg1, aArg2, aArg3: Int64);<br>
begin<br>
a := aArg1;<br>
b := aArg2;<br>
c := aArg3;<br>
end;<br>
<br>
function TMyList.GetPtrEnumerator: specialize TEnumerator<PT>;<br>
begin<br>
Result := inherited GetPtrEnumerator();<br>
end;<br>
<br>
{ with this you can simply do "for PtrTypeVar in
List.GetPtrEnumerator do",<br>
though this *might* not work in mode Delphi... }<br>
operator Enumerator(aEnum: specialize
TEnumerator<TMyList.PT>): specialize
TEnumerator<TMyList.PT>;<br>
begin<br>
Result := aEnum;<br>
end;<br>
<br>
var<br>
l: TMyList;<br>
r: TRec;<br>
p: TMyList.PT;<br>
begin<br>
l := TMyList.Create;<br>
l.Add(TRec.Create(1, 2, 3));<br>
l.Add(TRec.Create(9, 8, 7));<br>
for p in l.GetPtrEnumerator do begin<br>
Writeln(p^.a, ' ', p^.b, ' ', p^.c);<br>
end;<br>
end.<br>
<br>
=== code end ===<br>
<br>
Regards,<br>
Sven<br>
</body>
</html>