Re: [fpc-pascal] How to avoid Copy

2024-01-02 Thread Amir--- via fpc-pascal

Thanks!

On 1/2/24 22:59, Sven Barth via fpc-pascal wrote:
Amir--- via fpc-pascal  schrieb am 
Mi., 3. Jan. 2024, 07:53:


Yeap! That is actually what I posted here (Feature Request)
.


My example allows you to access it right now with the existing FPC 
release.


Regards,
Sven


___
fpc-pascal maillist  -fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] How to avoid Copy

2024-01-02 Thread Sven Barth via fpc-pascal
Amir--- via fpc-pascal  schrieb am Mi., 3.
Jan. 2024, 07:53:

> Yeap! That is actually what I posted here (Feature Request)
> .
>

My example allows you to access it right now with the existing FPC release.

Regards,
Sven

>
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] How to avoid Copy

2024-01-02 Thread Amir--- via fpc-pascal
Yeap! That is actually what I posted here (Feature Request) 
.


 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)
  public
    function GetPtrEnumerator: specialize TEnumerator;
  end;

constructor TRec.Create(aArg1, aArg2, aArg3: Int64);
begin
  a := aArg1;
  b := aArg2;
  c := aArg3;
end;

function TMyList.GetPtrEnumerator: specialize TEnumerator;
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): 
specialize TEnumerator;

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
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] How to avoid Copy

2024-01-02 Thread Sven Barth via fpc-pascal

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  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;
  TMyPtrList = specialize TList;

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)
  public
    function GetPtrEnumerator: specialize TEnumerator;
  end;

constructor TRec.Create(aArg1, aArg2, aArg3: Int64);
begin
  a := aArg1;
  b := aArg2;
  c := aArg3;
end;

function TMyList.GetPtrEnumerator: specialize TEnumerator;
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): 
specialize TEnumerator;

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


Re: [fpc-pascal] How to avoid Copy

2024-01-02 Thread Sven Barth via fpc-pascal

Am 01.01.2024 um 16:51 schrieb Wayne Sherman via fpc-pascal:

On Mon, Jan 1, 2024 at 6:14 AM Hairy Pixels wrote:

On Jan 1, 2024, at 3:50 PM, Michael Van Canneyt wrote:
You can't optimize that. As said, a generic is convenient but slow.

I don't know about that. Like was mentioned the enumerator needs to return a
pointer, preferable without ^ so it feels like a record and only use that in 
the for-in
scope. You can kind of do that yourself but it's cumbersome to maintain and
missing from the RTL (maybe for this reason).

Modern Pascal compilers already do this for certain types and pass by
reference parameters.  For example, AnsiString variables and variables
of type class are handled internally as pointers, but manipulated
opaquely without explicit pointer notation.  A record passed as a var
or constref parameter is internally a pointer, but does not require
the code writer to handle it explicitly as such.
That kind of concept only exists in Pascal for parameters, but not for 
variables or result values as would be needed for this.


Regards,
Sven___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] How to avoid Copy

2024-01-01 Thread Wayne Sherman via fpc-pascal
On Mon, Jan 1, 2024 at 6:14 AM Hairy Pixels wrote:
> > On Jan 1, 2024, at 3:50 PM, Michael Van Canneyt wrote:
> > You can't optimize that. As said, a generic is convenient but slow.
>
> I don't know about that. Like was mentioned the enumerator needs to return a
> pointer, preferable without ^ so it feels like a record and only use that in 
> the for-in
> scope. You can kind of do that yourself but it's cumbersome to maintain and
> missing from the RTL (maybe for this reason).

Modern Pascal compilers already do this for certain types and pass by
reference parameters.  For example, AnsiString variables and variables
of type class are handled internally as pointers, but manipulated
opaquely without explicit pointer notation.  A record passed as a var
or constref parameter is internally a pointer, but does not require
the code writer to handle it explicitly as such.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] How to avoid Copy

2024-01-01 Thread Hairy Pixels via fpc-pascal


> On Jan 1, 2024, at 3:50 PM, Michael Van Canneyt  
> wrote:
> 
> You can't optimize that. As said, a generic is convenient but slow.

I don't know about that. Like was mentioned the enumerator needs to return a 
pointer, preferable without ^ so it feels like a record and only use that in 
the for-in scope. You can kind of do that yourself but it's cumbersome to 
maintain and missing from the RTL (maybe for this reason).

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] How to avoid Copy

2024-01-01 Thread Michael Van Canneyt via fpc-pascal



On Mon, 1 Jan 2024, Hairy Pixels via fpc-pascal wrote:





On Dec 30, 2023, at 3:18 PM, Michael Van Canneyt via fpc-pascal 
 wrote:

And try to avoid "for r in data" because it will create a copy for each element
in the list.


This seems more like bad compiler design than anything.  From the users
perspective a for loop is a read-only operation and shouldn't be copying
anything.  I know it's how the enumerator thing is done but that may have
been a bad design from the start or needs some extra optimization
somewhere.


You can't optimize that. As said, a generic is convenient but slow.



For OP I would just kill the for-in loop in favor of a normal for-do loop and 
be done with the problem.


That will not help, since he is using a TList, it cannot work without
copy: any read implies a copy. Unless you use a pointer as Marco and I 
suggested.

Michael.___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] How to avoid Copy

2023-12-31 Thread Hairy Pixels via fpc-pascal


> On Dec 30, 2023, at 3:18 PM, Michael Van Canneyt via fpc-pascal 
>  wrote:
> 
> And try to avoid "for r in data" because it will create a copy for each 
> element
> in the list.

This seems more like bad compiler design than anything. From the users 
perspective a for loop is a read-only operation and shouldn't be copying 
anything. I know it's how the enumerator thing is done but that may have been a 
bad design from the start or needs some extra optimization somewhere.

For OP I would just kill the for-in loop in favor of a normal for-do loop and 
be done with the problem.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] How to avoid Copy

2023-12-31 Thread Michael Van Canneyt via fpc-pascal



On Sun, 31 Dec 2023, Amir--- via fpc-pascal wrote:




On 12/31/23 02:46, Marco van de Voort via fpc-pascal wrote:


Op 31/12/2023 om 04:11 schreef Amir--- via fpc-pascal:


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.
I believe the issue could be resolved if we make 
"TCustomListWithPointers.GetPtrEnumerator" a public method. Then, one 
can implement the following function:


 I also do this (an enumerator using a pointer type) in one of my 
container types. It also makes assignment in the for in possible. Note 
though that it is not necessarily needed to switch the whole 
collection to use a pointer, defining a separate iterator (e.g. 
collection.pointeriterator) that returns an iterator instantiated for 
^T.   You can then select the iterator by using for element in 
collection.iterator do.
Understand (and this is what I ended-up doing). I guess my question is 
that why we do not to have a PointerIterator in TList class.


Because no-one thought to implement it, or no-one saw the need.

Michael.___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] How to avoid Copy

2023-12-31 Thread Amir--- via fpc-pascal



On 12/31/23 02:46, Marco van de Voort via fpc-pascal wrote:


Op 31/12/2023 om 04:11 schreef Amir--- via fpc-pascal:


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.
I believe the issue could be resolved if we make 
"TCustomListWithPointers.GetPtrEnumerator" a public method. Then, one 
can implement the following function:


 I also do this (an enumerator using a pointer type) in one of my 
container types. It also makes assignment in the for in possible. Note 
though that it is not necessarily needed to switch the whole 
collection to use a pointer, defining a separate iterator (e.g. 
collection.pointeriterator) that returns an iterator instantiated for 
^T.   You can then select the iterator by using for element in 
collection.iterator do.
Understand (and this is what I ended-up doing). I guess my question is 
that why we do not to have a PointerIterator in TList class.


Amir

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] How to avoid Copy

2023-12-31 Thread Marco van de Voort via fpc-pascal


Op 31/12/2023 om 04:11 schreef Amir--- via fpc-pascal:


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.
I believe the issue could be resolved if we make 
"TCustomListWithPointers.GetPtrEnumerator" a public method. Then, one 
can implement the following function:


 I also do this (an enumerator using a pointer type) in one of my 
container types. It also makes assignment in the for in possible.  Note 
though that it is not necessarily needed to switch the whole collection 
to use a pointer, defining a separate iterator (e.g. 
collection.pointeriterator) that returns an iterator instantiated for 
^T.   You can then select the iterator by using for element in 
collection.iterator do.


The rtl-generics collections already have multiple iterators since for 
maps it is possible to iterate over the keys, the values or 
TPair

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] How to avoid Copy

2023-12-30 Thread Sven Barth via fpc-pascal
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.

Note: prefer the use of "const" instead of "constref" unless you really
need a reference, because the compiler will then pick the optimal way to
pass the value (e.g. small ones will be passed by register instead of as an
address).

Regards,
Sven

>
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] How to avoid Copy

2023-12-30 Thread Michael Van Canneyt via fpc-pascal



On Fri, 29 Dec 2023, Amir via fpc-pascal wrote:




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?


It means you use 
constref  data: TListOfMyRecord
or 
var data: TListOfMyRecord


And try to avoid "for r in data" because it will create a copy for each element
in the list.

I don't know how you defined TListOfMyRecord, but if it is a
generic TList, better change it to a TArray or Array
of TMyRecord, in which case you can use a pointer to the consecutive records
in the array. Generics are convenient, but slow.

Michael.___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] How to avoid Copy

2023-12-29 Thread Amir via fpc-pascal
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?



Regards,



Adriaan van Os


___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] How to avoid Copy

2023-12-29 Thread Adriaan van Os via fpc-pascal

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.

Regards,

Adriaan van Os
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal