TOM
 
Method Forwarding
 
 
TOM Home
TOM Tasks

FAQ
News
Highlights
Publications
Documentation
Download TOM
TOM Software
Bug Database
Mailing Lists

Mail:
tiggr at gerbil.org

Short Cuts:
Tesla
TOM/Gtk
GP
MU

Snapshots:
all of 'em
tom [an error occurred while processing this directive]
tesla [an error occurred while processing this directive]
mu [an error occurred while processing this directive]
tomgtk [an error occurred while processing this directive]

Released:
all of 'em
tom 1.1.1
tomgtk 0.11
tesla 0.91
gp 0.5
mu 1.0

Misc:
GIF free NOW!

One of the things that make dynamic binding a very interesting approach to method dispatching (i.e., the decision which code to invoke to handle a given message) is the ability to forward a method invocation. A message is forwarded when the receiver object does not provide an implementation of the method denoted by the message. In this highlight, the forward mechanism of TOM will be explained.

Every method has two implicit arguments: self and cmd. The object self is the `current object'; it is the receiver of the message that lead to the method invocation. The argument cmd is the selector of that message (see highlight `The selector type').

When an object does not implement a method, the self and cmd arguments are used to decide how to respond to the message.

  • The receiver is asked for its forwardDelegate. If an object other than self is returned, the message is simply resent to that object. This is the mechanism of choice when a lot of methods are to be delegated to few different objects.

    The forwardDelegate method is implemented by the instance All, and thus (by convention) implemented by all classes and instances. The default implementation simply returns self:
    
    All (self)
      forwardDelegate selector sel
    {
    }
    

  • (Skip this item if you don't understand it.) If the object implements the method
    
    InvocationResult
      forwardSelector selector sel
            arguments pointer pap;
    
    then that method is invoked, with sel being the selector of the message being forwarded, and pap a pointer to a va_list for the arguments in the message. The InvocationResult that is returned defines the values to be returned from the invocation that is being forwarded.

    Note that an object implementing a method foo is different from an object respoding TRUE when asked respondsTo. The latter can be overridden while the former is a direct check, which is much faster.

    This seemingly nasty low-level approach is used for fast dispatching of too.RemoteProxy method invocations and for invocations on curried tom.Invocation objects.

  • With everything having failed so far, the whole invocation is packed into a newly created Invocation object, and sent to the receiver with a forwardInvocation method. The receiver can then decide what to do with it. The default implementation by the instance All raises a program-condition for the selector and target of the Invocation:
    
    InvocationResult
      forwardInvocation Invocation invocation
    {
      [[SelectorCondition
        for self class program-condition
        message "unrecognized selector"
        selector [invocation selector]]
       raise];
    }
    
    The forwardInvocation mechanism can also be used to mimic the functionality of the forwardDelegate method. If the forwardDelegate would return the object my_delegate, the following method would provide identical functionality:
    
    InvocationResult
      forwardInvocation Invocation invocation
    {
      = [invocation fireAt my_delegate];
    }
    

Speed

Everything comes with a price, especially flexibility.

I've done some speed tests (on a PII/266, Debian 1.3.x, gcc 2.7.2.1) with various ways to invoke a method, with various number of arguments, with the results shown in the table below.

  • `x' is a direct method invocation,
    
    [foo do (1, 2)];
    
  • `p' is a perform
    
    [foo perform selector ("v_do_(ii)") with (1, 2)];
    
  • `d' is through a forwardDelegate. The invocation looks like that of `x', but it is invoked on an object that does not implement the method, but does provide the following method (an instance of Sub does implement the desired method).
    
    Sub
      forwardDelegate selector s
    {
      = fwd_delegate;
    }
    
  • `i' is through a forwardInvocation. Similar to `d', but instead of forwardDelegate, the following method is implemented:
    
    InvocationResult
      forwardInvocation Invocation invocation
    {
      = [invocation fireAt fwd_delegate];
    }
    
how #inv time
0 1 2 3 4 5 6
x 10^8 12.58 12.93 12.58 12.53 13.69 14.00 13.91
p 10^7 11.15 11.77 12.74 13.80 14.88 15.63 17.32
d 10^7 11.62 12.75 13.75 15.11 15.81 16.61 18.52
i 10^6 15.96 17.44 17.80 18.14 18.51 18.87 19.22

Legend:
howHow the method is invoked
#invThe number of invocations performed in the given time.
timeThe CPU time needed to run the test for the indicated number of arguments.

Note that for every forwardInvocation dispatch, an Invocation object and an InvocationResultis created, and approximately half the time taken by the test runs is spent in garbage collecting those objects. Also note that the mechanism underlying perform with and forwardDelegate, does not create InvocationResult objects for void methods, as was the case in the test.

The numbers come down to the following: in the time that you can do 1 forwardInvocation, you can do 10 forwardDelegate calls or invocations through perform with, and 100 direct method invocations.

I don't know if these numbers are good or bad; I don't have numbers to compare them with (maybe testing Objective-C Rhapsody on a PII/266?). It does show, that using forwardDelegate is much faster than having an Invocation object be created and forwarding that. Which is what it was supposed to do. (And it can probably be made much faster when using __builtin_apply_args in trt_forward, instead of going through perform_args.)


Up: Highlights
 
Copyright © 1997-2002 Programmers Without Deadlines