[fpc-pascal] Are there any drawbacks to "reference to"?

Sven Barth pascaldragon at googlemail.com
Sun Jun 19 17:04:05 CEST 2022


Hairy Pixels <genericptr at gmail.com> schrieb am So., 19. Juni 2022, 15:44:

>
>
> > On Jun 19, 2022, at 7:01 PM, Sven Barth via fpc-pascal <
> fpc-pascal at lists.freepascal.org> wrote:
> >
> > As I can see neither Anthony's nor Ryan's mail, but I can see them in
> the archive, I'll use Jonas mail for some general replies (please CC me
> when replying):
>
> Firstly unrelated, I posted a question about generics which you probably
> have the answer to and may have missed (
> https://www.mail-archive.com/fpc-pascal@lists.freepascal.org/msg55415.html).
> I just joined the Lazarus forum also and mentioned this in a related topic
> where another user seems to have stumbled on something similar.
>

I have replied on the forum. But if you want a response to your other mail
please reply to your own mail with me in CC, cause I can see it only in the
archive.


> > Also keep in mind that the expensive creation only happens once.
> Afterwards it's "only" the price of an indirect call.
> >
>
> The performance problem will surfice if you use a reference inside a
> function which is called often. Lets say you decided to use a reference as
> a general callback type because it’s convenient not to consider “is nested”
> or “of object” and then call this from within a function which is called in
> a tight loop.
>
> Depending on what you’re doing this extra allocation of the interface
> could totally blow your program out of the water but this isn’t apparent to
> the programer who merely sees what they’re doing a simple callback. For
> this reason you can’t rely on references as an all purpose callback type
> and if performance is important you need to make duplicate functions which
> take different types of functions pointers, hence our desire for a
> generalized function pointer that that doesn’t do the interface allocation
> if there’s no passing of the reference outside the calling scope.
>

You misunderstand how function references work.

Assume the following:

=== code begin ===

function Foo: LongInt;
var
  f: reference to procedure;
  sum, i: LongInt;
begin
  f := procedure
  begin
    sum := sum + i * 2;
  end;
  sum := 0;
  for i := 0 to 100 do
    f();
  Result := sum;
end;

=== code end ===

This is essentially equivalent to the following:

=== code begin ===

function Foo: LongInt;
type
  TFunc = interface
    procedure Invoke;
  end;
  TCapturer = class(TInterfacedObject, TFunc)
    sum, i: LongInt;
    procedure TFunc.Invoke =TFunc_Invoke;
    procedure TFunc_Invoke;
  end;

  procedure TCapturer.TFunc_Invoke;
  begin
    sum := sum + i * 2;
  end;

var
  capturer: TCapturer;
  keepalive: IInterface;
  f: TFunc;
begin
  capturer := TCapturer.Create;
  keepalive := capturer;

  f := capturer;
  for capturer.i := 0 to 100 do
    f.Invoke();
  Result := capturer.sum;
end;

=== code end ===

As you can see the allocation only happens once and not all the time.
What might be worse however is the optimization behavior as in this example
the compiler wouldn't optimize the counter variable into a regvar with
enabled optimizations (however in case of a nested function the compiler
wouldn't do this either if the function isn't inlined).

And even if you do the assignment of the function variable inside the loop
you "merely" get the reference counting as a side effect and not a whole
allocations.

Regards,
Sven
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freepascal.org/pipermail/fpc-pascal/attachments/20220619/7750704a/attachment.htm>


More information about the fpc-pascal mailing list