Hi,

The trick with enumerators is to never make them classes, and use advanced records instead, I've found. This way you avoid the heap allocation and the implicit try/finally. Also make sure you inline the MoveNext and GetCurrent!

that's what I do.


But the generated assembly is still worse than an old for loop, because it keeps all the fields of the record in memory.

for example

>   for I in TSlice<SizeUInt>.TakeWhile(Arr, Test) do J := I;

generates something like this

0000000000401290 488b45f0                 mov -0x10(%rbp),%rax
0000000000401294 488b00                   mov    (%rax),%rax
0000000000401297 488905b22a0300           mov %rax,0x32ab2(%rip)        # 0x433d50 <U_$P$PROJECT1_$$_I>
project1.lpr:75                           J := I;
000000000040129E 488905bb2a0300           mov %rax,0x32abb(%rip)        # 0x433d60 <U_$P$PROJECT1_$$_J> project1.lpr:74                           for I in TSlice<SizeUInt>.TakeWhile(Arr, Test) do
00000000004012A5 488345f008               addq   $0x8,-0x10(%rbp)
project1.lpr:69                           begin
00000000004012AA 488b45e8                 mov    -0x18(%rbp),%rax
project1.lpr:74                           for I in TSlice<SizeUInt>.TakeWhile(Arr, Test) do
00000000004012AE 483b45f0                 cmp    -0x10(%rbp),%rax
00000000004012B2 720a                     jb     0x4012be <main+334>
00000000004012B4 483b45e0                 cmp    -0x20(%rbp),%rax
00000000004012B8 7404                     je     0x4012be <main+334>
00000000004012BA b001                     mov    $0x1,%al
00000000004012BC eb02                     jmp    0x4012c0 <main+336>
00000000004012BE 30c0                     xor    %al,%al
00000000004012C0 84c0                     test   %al,%al
00000000004012C2 75cc                     jne    0x401290 <main+288>

Nearly every line is accessing some memory, when it could keep everything in a few registers. amd64 has 16 registers, but fpc seems to only know three when records are involved



Cheers,
Benito

Am 22.02.19 um 16:51 schrieb Ben Grasset:
On Fri, Feb 22, 2019 at 1:07 AM Paul van Helden <p...@planetgis.co.za <mailto:p...@planetgis.co.za>> wrote:

     How do you make a (for in) enumerator with a record? I don't use
    them for exactly this reason, and they did seem to be another
    useful language feature that turned out to be poorly implemented
    by Embarcadero. (Haven't checked with FPC).


Here's an example (for FPC) that demonstrates it by implementing the "take-while" pattern:

program TakeWhileExample;

{$mode Delphi}{$H+}{$J-}
{$modeswitch NestedProcVars}
{$ImplicitExceptions Off}
{$PointerMath On}

type
  TSlice<T> = record
  public type
    PT = ^T;
    ArrayType = array of T;
  private
    FFirst, FLast, FCurrent: PT;
    function GetCurrent: T; inline;
  public
    function GetEnumerator: TSlice<T>; inline;
    function MoveNext: Boolean; inline;
    class function TakeWhile(const A: ArrayType; function F(const Val: T): Boolean): TSlice<T>; static; inline;
    property Current: T read GetCurrent;
  end;

  TTestFunc<T> = function(const Val: T): Boolean;

  function TSlice<T>.GetCurrent: T;
  begin
    Result := FCurrent^;
  end;

  function TSlice<T>.GetEnumerator: TSlice<T>;
  begin
    Result := Self;
    with Result do FCurrent := FFirst - 1;
  end;

  function TSlice<T>.MoveNext: Boolean;
  begin
    Inc(FCurrent);
    Exit((FCurrent <= FLast) and (FFirst <> FLast));
  end;

  function Test(const Val: SizeUInt): Boolean; inline;
  begin
    Exit((Val < 50000000));
  end;

  class function TSlice<T>.TakeWhile(const A: ArrayType; function F(const Val: T): Boolean): TSlice<T>;
  var
    I: SizeUInt;
    X: TTestFunc<T> absolute F;
    //FPC generates slightly better code for the "absolute" way, not sure why...
  begin
    with Result do begin
      FFirst := @A[0];
      FLast := @A[0];
      for I := 0 to High(A) do
        case X(A[I]) of
          True: Inc(FLast);
          False: Exit();
        end;
    end;
  end;

var
  I, J: SizeUInt;
  Arr: TSlice<SizeUInt>.ArrayType;

begin
  SetLength(Arr, 100000000);
  for I := 0 to 99999999 do Arr[I] := I;
  I := 0;
  J := 0;
  for I in TSlice<SizeUInt>.TakeWhile(Arr, Test) do J := I;
  WriteLn(J);
end.

_______________________________________________
fpc-devel maillist  -  fpc-devel@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
_______________________________________________
fpc-devel maillist  -  fpc-devel@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

Reply via email to