Nat! bio photo


Senior Mull

Twitter Github Twitch

mulle-objc: removing superflous ifs

Continued from mulle-objc: a meta calling convention

Now that the three parameter message sending function has been introduced, lets try to milk some benefit out of it.

mulle-objc is conceptually based off the Apple/NeXT runtime, my apologies for ignoring the GNU-Runtimes in all these little expositions.

The current 64 bit Intel implementation of the Apple runtime objc_msgSend starts like this:

0x7fff90ef9080:  testq  %rdi, %rdi
0x7fff90ef9083:  je     0x7fff90ef90f8
0x7fff90ef9086:  testb  $0x1, %dil
0x7fff90ef908a:  jne    0x7fff90ef910f

That is basically the nil check against self

if( ! self)
   return( self);

followed by a tagged pointer check

if( (intptr_t) self & 1)
   goto is_tagged_pointer;

These checks are always done. Tagged pointers and their merit may be discussed at a later time. Here I focus solely on the nil check, which very often is superflous.

Often self is known to be non-nil

Let’s see when and why it is superflous, by looking at a regular ObjC loop:

+ (void) doArray:(NSArray *) array
   NSEnumerator   *rover;
   id             obj;

   rover = [array objectEnumerator];
   while( obj = [rover nextObject])
      [obj doStuff];

The compiler knows, that obj is non-nil, when it is calling -doStuff, since it just checked for non-nilness in the while statement.

Because methods are now uniform, one can use a static inline function as a trampoline to the actual extern function. This is not possible without severe performance degradations in normal ObjC:

static inline void  *mulle_objc_object_call( void *self,
                               SEL _cmd,
                               void *_param)
   extern void   *mulle_objc_object_call_2( void *self,
                               SEL _cmd,
                               void *_param);
   if( ! self)
      return( NULL);
   return( mulle_objc_object_call_2( self, _cmd, _param);

The optimizing compiler can now optimize the ! self check away. The result will be less overhead spent in message sending, since mulle_objc_object_call_2 would not have this check. The expense is two additional instructions are inlined per method call, when the nil check can not be optimized away.

Generally all calls inside methods to self also should be optimizable, since self is (or should be) known to be non-nil.

Food for thought

  1. Maybe there is even more to inline ? :)
  2. How many method calls are actually in a typical program ?
  3. Can the compiler use different inlining strategies based on compiler flags ?
  4. What happens when an object is messaged again ?

Continue to mulle-objc: inlined messaging

Post a comment

All comments are held for moderation; basic HTML formatting accepted.

E-mail: (not published)