Interface performance

classic Classic list List threaded Threaded
25 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Interface performance

Ryan Joseph
Some times when I want to communicate with a class I don’t have full scope access to I’ll use interfaces and the Supports function to call a method. I’ve noticed however that the string compare function that it is used to find the interface in the class is very slow and makes them not useable for high performance situations. Is there a better way to do this or should I not use interfaces like this in FPC?

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface performance

Ryan Joseph
I did some experimenting this morning and found out I could pass references to the interface and call methods directly without using Supports and incurring the string compare penalty. There’s also interface delegation I read about and using “implements” keyword but I couldn’t understand what the purpose of this is and why it’s even useful.

> On Nov 10, 2016, at 6:45 PM, Ryan Joseph <[hidden email]> wrote:
>
> Some times when I want to communicate with a class I don’t have full scope access to I’ll use interfaces and the Supports function to call a method. I’ve noticed however that the string compare function that it is used to find the interface in the class is very slow and makes them not useable for high performance situations. Is there a better way to do this or should I not use interfaces like this in FPC?

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface performance

Tony Whyman
Ryan,

You'll find a couple threads earlier this year in heated debate about
interface delegation and how it is implemented. My conclusion was to
avoid interface delegation - there are just too many traps for the unwary.

There is also another thread bemoaning some odd features about the
"supports" function.

In use, once you have a variable containing a reference to an interface,
it behaves, for the most part, the same as a reference to an object
supporting the same methods and properties and should be used as such.

Tony Whyman

MWA


On 11/11/16 08:25, Ryan Joseph wrote:

> I did some experimenting this morning and found out I could pass references to the interface and call methods directly without using Supports and incurring the string compare penalty. There’s also interface delegation I read about and using “implements” keyword but I couldn’t understand what the purpose of this is and why it’s even useful.
>
>> On Nov 10, 2016, at 6:45 PM, Ryan Joseph <[hidden email]> wrote:
>>
>> Some times when I want to communicate with a class I don’t have full scope access to I’ll use interfaces and the Supports function to call a method. I’ve noticed however that the string compare function that it is used to find the interface in the class is very slow and makes them not useable for high performance situations. Is there a better way to do this or should I not use interfaces like this in FPC?
> Regards,
> Ryan Joseph
>
> _______________________________________________
> fpc-pascal maillist  -  [hidden email]
> http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
>

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface performance

Ryan Joseph

> On Nov 11, 2016, at 4:31 PM, Tony Whyman <[hidden email]> wrote:
>
> You'll find a couple threads earlier this year in heated debate about interface delegation and how it is implemented. My conclusion was to avoid interface delegation - there are just too many traps for the unwary.

I just don’t plain get it. The examples given seem to be a redundant property that could be replaced with a reference the interface itself.

>
> There is also another thread bemoaning some odd features about the "supports" function.

The string lookup absolutely murders performance (I was using “cobra” interfaces) so it can’t be used in some situations.

>
> In use, once you have a variable containing a reference to an interface, it behaves, for the most part, the same as a reference to an object supporting the same methods and properties and should be used as such.

Just as I got this message I’m running into some inexplicable memory related crashes casting from interfaces to objects and calling methods.  Are you sure you can just cast these around like that? The compiler seems to get confused and lose track of what the type actually is. In my first test this didn’t happen and I was able to use an interface as a type then cast it to an object, pass to a function and call a method. In the 2nd example the variable went from a couple classes first and died at the end. Seems very unstable.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface performance

Tony Whyman
On 11/11/16 09:46, Ryan Joseph wrote:
> Just as I got this message I’m running into some inexplicable memory related crashes casting from interfaces to objects and calling methods.  Are you sure you can just cast these around like that? The compiler seems to get confused and lose track of what the type actually is. In my first test this didn’t happen and I was able to use an interface as a type then cast it to an object, pass to a function and call a method. In the 2nd example the variable went from a couple classes first and died at the end. Seems very unstable.
You will probably need to post some examples to get more help. But here
are some simple rules:

1. Classes that provide interfaces in most cases will have
TInterfacedObject as their ancestor.

2. If you have an object with a class definition that explicitly
supports a given interface then to get an interface reference all you
need to do is to assign it to a variable of the interface type.

3. To get an object back from an interface you must use the "as" operator.

e.g.

type
  IMyInterface = interface
     procedure MyProc;
  end;

  TMyObjectClass = class(TInterfacedObject,IMyInterface)
  public
    procedure MyProc;
  end;

var MyObject1, MyObject2: TMyObjectClass;
       MyInterface: IMyInterface;

begin
   MyObject1 := TMyObjectClass.Create;
   MyInterface := MyObject1;

   MyInterface.MyProc;

   MyObject2 := MyInterface as TMyObjectClass;

   MyObject1.Free {OK for CORBA - will cause an exception in COM}
   {MyObject1, MyObject2 and MyInterface are now all invalid}
end;
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface performance

Tony Whyman
In reply to this post by Ryan Joseph
On 11/11/16 09:46, Ryan Joseph wrote:
> I just don’t plain get it. The examples given seem to be a redundant property that could be replaced with a reference the interface itself.
Interface delegation is really just an optimisation and is equivalent to
defining a set of methods to support the interface and each one then
calls the same method in the "delegated" interface. IMHO, extending the
definition of a property for this optimisation isn't exactly intuitive.

Delegated interfaces cause even more problems with reference counted COM
interfaces. Unless you are really confident in your use of interfaces -
just don't go there.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface performance

Ryan Joseph
In reply to this post by Tony Whyman

> On Nov 11, 2016, at 4:56 PM, Tony Whyman <[hidden email]> wrote:
>
> 3. To get an object back from an interface you must use the "as" operator.

This step I was not doing and the “as” operator from an earlier showed it uses a string lookup anyways so it defeated the purpose in some cases (but better than my previous solution using Supports for all access).

I’m trying your code example now and I get "Class or COM interface type expected, but got “IMyInterface”” when I try to cast with “as”. I was using {$interfaces corba} so maybe that’s the problem?



Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface performance

Ryan Joseph
In reply to this post by Tony Whyman

> On Nov 11, 2016, at 4:56 PM, Tony Whyman <[hidden email]> wrote:
>
> 3. To get an object back from an interface you must use the "as" operator.

Another point to do with CORBA. The manual says they are not reference counted and programmer needs to do book keeping. The crash I was getting was in line with accessing memory that has been deallocated so perhaps because I didn’t do my “book keeping” it was freed as it passed scopes. Not sure how to manage reference counting for interfaces though because the docs didn’t actually explain this.

Maybe the better question is should I ditch CORBA in favor or COM?


Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface performance

Tony Whyman
On 11/11/16 10:25, Ryan Joseph wrote:

>> On Nov 11, 2016, at 4:56 PM, Tony Whyman <[hidden email]> wrote:
>>
>> 3. To get an object back from an interface you must use the "as" operator.
> Another point to do with CORBA. The manual says they are not reference counted and programmer needs to do book keeping. The crash I was getting was in line with accessing memory that has been deallocated so perhaps because I didn’t do my “book keeping” it was freed as it passed scopes. Not sure how to manage reference counting for interfaces though because the docs didn’t actually explain this.
>
> Maybe the better question is should I ditch CORBA in favor or COM?
>
>
> Regards,
> Ryan Joseph
>
> _______________________________________________
> fpc-pascal maillist  -  [hidden email]
> http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
>
If last question doesn't send the list into overdrive then nothing will;)

With CORBA you are responsible for freeing the objects that provide an
interface in the same way that you are always responsible for freeing
the objects that you create. If you free an object before you finish
using it then it's a bug and using interfaces does not change that.

With COM, once you assign the object providing the interface to a
variable with an interface type then you are no longer responsible for
freeing the object. The object is now managed by the system and if you
do free it then you get an exception. It's really the same principle as
AnsiStrings and dynamic arrays, which are also reference counted objects.

The classic newbie problem with COM comes when you have an interface
with a method that returns another interface. If the underlying objects
"know" about each other and reference each other's data then the order
in which they are freed becomes important and automatic reference
counting does not always give the right answer. There are design
techniques to manage this, such as objects keeping interface references
to other objects - but then you have to be careful to avoid circular
references that prevent the objects ever being released.

I would start working with interfaces by using CORBA. It's more obvious
what is going on and fewer traps for the unwary. However, if you are
designing a standard package providing an API realised as a Pascal
Interface then I would argue that a properly designed COM based
interface is easier for the API user, in the same way that AnsiStrings
just "work".
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface performance

Tony Whyman
In reply to this post by Ryan Joseph
On 11/11/16 10:08, Ryan Joseph wrote:
> I’m trying your code example now and I get "Class or COM interface type expected, but got “IMyInterface”” when I try to cast with “as”. I was using {$interfaces corba} so maybe that’s the problem?
Ooops, as you may guess, I typically work with COM interfaces.

Someone else may correct me, but with CORBA, I believe you have to
explicitly add a function to the interface such as

function GetObject: TMyObject;

and implement as

function TMyObject.GetObject: TMyObject;
begin
   Result := self;
end;

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface performance

Ryan Joseph
In reply to this post by Tony Whyman

> On Nov 11, 2016, at 5:44 PM, Tony Whyman <[hidden email]> wrote:
>
> With CORBA you are responsible for freeing the objects that provide an interface in the same way that you are always responsible for freeing the objects that you create. If you free an object before you finish using it then it's a bug and using interfaces does not change that.

I’m happy to use CORBA but that means I can’t cast an interface back to an object using “as” but I’m not sure that’s the reason I was getting that crash (I need to make a test example I guess) but you seem to think “as” is needed but CORBA doesn’t allow this.

Tried to switch  CORBA interface to a COM now and got another strange crash accessing memory.

What does memory management even mean for interfaces? I never allocate an interface I just implement it in a class so what’s there to be freed? All these crashes I’m getting suggest memory is being trashed by the compiler at some point without my knowledge. I never explicitly allocate an interface like an object so there’s nothing to manage in my mind.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface performance

Graeme Geldenhuys-6
In reply to this post by Ryan Joseph
On 2016-11-11 09:46, Ryan Joseph wrote:
> The string lookup absolutely murders performance (I was using “cobra”
> interfaces) so it can’t be used in some situations.

If you are talking about the GUID, I'm not sure if you know, but when
using FPC's CORBA interfaces, you can use a MUCH shorter string which
gives better performance. MSEide even has built-in support to do just
that (generate smaller GUID values of about 6 characters long).

Regards,
  Graeme

--
fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal
http://fpgui.sourceforge.net/

My public PGP key:  http://tinyurl.com/graeme-pgp
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface performance

Ryan Joseph
In reply to this post by Tony Whyman

> On Nov 11, 2016, at 5:52 PM, Tony Whyman <[hidden email]> wrote:
>
> Someone else may correct me, but with CORBA, I believe you have to explicitly add a function to the interface such as
>
> function GetObject: TMyObject;
>
> and implement as
>
> function TMyObject.GetObject: TMyObject;
> begin
>  Result := self;
> end;

I think I’m out of time now but I’ll try this in my code that was crashing before.

How is this different from just casting the interface to an object? There’s your example below basically like I was doing it before with the seemingly random crashes. Isn’t MyObject2 still pointing to MyObject1?

type
IMyInterface = interface
   procedure MyProc;
end;

TMyObjectClass = class(TInterfacedObject,IMyInterface)
public
  procedure MyProc;
end;

var MyObject1, MyObject2: TMyObjectClass;
     MyInterface: IMyInterface;

begin
 MyObject1 := TMyObjectClass.Create;
 MyInterface := MyObject1;

 MyInterface.MyProc;

 MyObject2 := TMyObjectClass(MyInterface);

 MyObject1.Free {OK for CORBA - will cause an exception in COM}
 {MyObject1, MyObject2 and MyInterface are now all invalid}
end;


Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface performance

Ryan Joseph
In reply to this post by Graeme Geldenhuys-6

> On Nov 11, 2016, at 6:08 PM, Graeme Geldenhuys <[hidden email]> wrote:
>
> If you are talking about the GUID, I'm not sure if you know, but when
> using FPC's CORBA interfaces, you can use a MUCH shorter string which
> gives better performance. MSEide even has built-in support to do just
> that (generate smaller GUID values of about 6 characters long).

I’m not sure all Supports does but I didn’t test shorter strings yet. I think it does more than just compare strings though. Maybe I’ll try that later thanks.


Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface performance

Tony Whyman
In reply to this post by Ryan Joseph
On 11/11/16 10:59, Ryan Joseph wrote:
> What does memory management even mean for interfaces? I never allocate an interface I just implement it in a class so what’s there to be freed? All these crashes I’m getting suggest memory is being trashed by the compiler at some point without my knowledge. I never explicitly allocate an interface like an object so there’s nothing to manage in my mind.
May be that the best way to understand an interface is to think of it as
a pointer to a class's VMT (or at least a VMT derived from the class's
VMT) plus a reference to the object 'self'). When you call a method that
belongs to an interface, the underlying code simply performs a VMT
lookup and calls the method at the selected table location and provides
the 'self' reference to the method. The result is then no different to a
normal method call.

Interfaces do exist independent of objects. They depend on underlying
object instances for their implementation.

An interface continues to work as long as the 'self' reference is valid.
Once the underlying object is freed then the 'self'' reference becomes
invalid and the interface no longer works.

With CORBA, you are responsible for creating and freeing the object
instances providing an interface. Under COM, you are still responsible
for creating the objects. They are automatically freed when the
interface (and any copies) go out of scope.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface performance

Martin Schreiber-2
In reply to this post by Graeme Geldenhuys-6
On Friday 11 November 2016 12:08:26 Graeme Geldenhuys wrote:
> On 2016-11-11 09:46, Ryan Joseph wrote:
> > The string lookup absolutely murders performance (I was using “cobra”
> > interfaces) so it can’t be used in some situations.
>
> If you are talking about the GUID, I'm not sure if you know, but when
> using FPC's CORBA interfaces, you can use a MUCH shorter string which
> gives better performance. MSEide even has built-in support to do just
> that (generate smaller GUID values of about 6 characters long).
>
The minimum are 2 characters. Seeing that, actually the minimum could be 1,
there is room for improvement. ;-)
GUID is a little bit excessive, I call them UID.

Martin
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface performance

Tony Whyman
In reply to this post by Ryan Joseph
>   Isn’t MyObject2 still pointing to MyObject1?
If you go back to the FPC documentation, in the User Guide it says
"Objects are stored in memory just as ordinary records with an extra
field: a pointer to the Virtual Method Table (VMT)." My understanding is
that an interface is stored similarly, except that a different VMT is
used i.e. that which defines the interface. If you try and assign one to
the other by just coercing the type then you will get undefined results.


On 11/11/16 11:25, Ryan Joseph wrote:

>> On Nov 11, 2016, at 5:52 PM, Tony Whyman <[hidden email]> wrote:
>>
>> Someone else may correct me, but with CORBA, I believe you have to explicitly add a function to the interface such as
>>
>> function GetObject: TMyObject;
>>
>> and implement as
>>
>> function TMyObject.GetObject: TMyObject;
>> begin
>>   Result := self;
>> end;
> I think I’m out of time now but I’ll try this in my code that was crashing before.
>
> How is this different from just casting the interface to an object? There’s your example below basically like I was doing it before with the seemingly random crashes. Isn’t MyObject2 still pointing to MyObject1?
>
> type
> IMyInterface = interface
>     procedure MyProc;
> end;
>
> TMyObjectClass = class(TInterfacedObject,IMyInterface)
> public
>    procedure MyProc;
> end;
>
> var MyObject1, MyObject2: TMyObjectClass;
>       MyInterface: IMyInterface;
>
> begin
>   MyObject1 := TMyObjectClass.Create;
>   MyInterface := MyObject1;
>
>   MyInterface.MyProc;
>
>   MyObject2 := TMyObjectClass(MyInterface);
>
>   MyObject1.Free {OK for CORBA - will cause an exception in COM}
>   {MyObject1, MyObject2 and MyInterface are now all invalid}
> end;
>
>
> Regards,
> Ryan Joseph
>
> _______________________________________________
> fpc-pascal maillist  -  [hidden email]
> http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
>

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface performance

Graeme Geldenhuys-6
In reply to this post by Tony Whyman
On 2016-11-11 10:52, Tony Whyman wrote:
> Someone else may correct me, but with CORBA, I believe you have to
> explicitly add a function to the interface such as
>
> function GetObject: TMyObject;


I may be wrong too, but I thought, for reliable results, you had to do
that for COM and CORBA style interfaces. Then again, I've used
Interfaces for 15+ years and once I have a interface reference, I never
needed to get back to the originating object. Maybe I was just lucky in
my use cases. ;-)

Regards,
  Graeme

--
fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal
http://fpgui.sourceforge.net/

My public PGP key:  http://tinyurl.com/graeme-pgp
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface performance

Sven Barth-2

Am 11.11.2016 15:16 schrieb "Graeme Geldenhuys" <[hidden email]>:
>
> On 2016-11-11 10:52, Tony Whyman wrote:
> > Someone else may correct me, but with CORBA, I believe you have to
> > explicitly add a function to the interface such as
> >
> > function GetObject: TMyObject;
>
>
> I may be wrong too, but I thought, for reliable results, you had to do
> that for COM and CORBA style interfaces. Then again, I've used
> Interfaces for 15+ years and once I have a interface reference, I never
> needed to get back to the originating object. Maybe I was just lucky in
> my use cases. ;-)

For COM interfaces there is support for the as-operator as every TObject has an implicit self reference interface that's returned by QueryInterface(). The raw CORBA interfaces (or internally called raw interfaces) don't have such a method thus no such as-operator.

Regards,
Sven


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface performance

Ryan Joseph
In reply to this post by Tony Whyman

> On Nov 11, 2016, at 8:54 PM, Tony Whyman <[hidden email]> wrote:
>
> If you go back to the FPC documentation, in the User Guide it says "Objects are stored in memory just as ordinary records with an extra field: a pointer to the Virtual Method Table (VMT)." My understanding is that an interface is stored similarly, except that a different VMT is used i.e. that which defines the interface. If you try and assign one to the other by just coercing the type then you will get undefined results.

I think what I’m trying to do is just not compatible with how interfaces work in FPC then. I’m trying to pass an object (which implements an interface) and then without using “as” or Supports access the interface for that object so I can call its methods. I keep running into crashes and from your description it’s because I only have an object but I need the interface reference which MUST use a string lookup. Casting an object as its interface causes undefined results.

It seems trivial to be avoiding a string lookup but in some tight loops and sorting functions the call to Supports is taking far more time than the actual sorting so it’s clearly not a good solution.

More on this tomorrow until I give up for certain. :)

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
12