[comp.lang.objective-c] Real-time coding practices in Objective C

barton@contel0 ( R+D Software Test ENG) (10/23/90)

Goal: Time critical code should perform as well as C code, while retaining the 
advantages of object-oriented design.  Time-critical code in our system includes
polling of hardware devices at a 10mS interval, and processing which will be
on the normal path of execution for peak volume of data items (phone calls).

Coding guidelines:
1. Declare the receiver of a method to be a specific type object, 
rather than an "id". This allows static binding of methods at compile time,
saving most of the 20uS overhead of dynamic lookup.
	Examples:	c_Dpom *vo_self = (c_Dpom *)self;
			P_Connect *vo_pktid= (P_Connect *)ao_pktid;

2. Don't use inherited methods. They cannot be statically bound. 
Inherited data is ok.

3. Don't nest method invocations, since the intermediate results are of 
type "id". Explicitly declare the intermediate variables.
	Example: replace this:
		[vo_class2 m_status:[vo_class1 m_status]];
		
		with
		
		some_value = [vo_class1 m_status];
		[vo_class2 m_status:some_value];

4. Avoid Stepstone library objects such as String, Collection, etc, 
since these do not follow the guidelines given above. 
Code your own objects, and re-use them.

5. Avoid using methods for simple instance variable access, for code within 
the object. Just directly access the instance variable.

Example: Pair of enqueue and dequeue methods, for an event object
to/from an event queue reduced from 200uS to 30uS (on our SPARC
processor board) by following these guidelines.  The queue was
re-implemented as a simple fixed size array, and explicit head and tail
pointers are maintained, rather than using a subclass of ordered
collection. I estimate 5-10 messenger calls were eliminated, each of
which takes about 20uS.

This kind of coding raises all kinds of questions:
	Could Stepstone provide an alternate version of runtime and ICpak101
	libraries, which was optimized for speed?
	
	Could static binding be improved to recognize the type of the receiver
	just from a cast, rather than the data declaration? Can the
	inherited methods be static bound, in code where static (compile-time)
	analysis shows that there is no method over-ride done by 
	the sub-classes?

	Does anyone have any other suggestions for speed improvement,
	which are specific to the Objective-C language (as opposed to
	just general-purpose good-programming practices)?

On un-related issues:
	Can the .h files be enhanced to declare certain methods as internal 
	or private to an object? I frequently have the case where the
	public interface is only a few methods, but for modularity reasons
	I have a dozen internal methods. At present they are all listed
	in the header, with only a comment to protect them from another
	programmer attempting to use them.
	
	If the function names the objc pre-processor generated were changed from
	"_23_class" (for example) to "_23_class_my_method_name" it would make them
	more readable, especially in dbxtools where the function name is displayed.

	matt

cox@stpstn.UUCP (Brad Cox) (10/26/90)

In article <129@contel0> barton@contel0.UUCP (Matthew Barton - R+D Software eng) writes:
| 	Does anyone have any other suggestions for speed improvement,
| 	which are specific to the Objective-C language (as opposed to
| 	just general-purpose good-programming practices)?

The approach you outlined seems quite sound, and admirably researched.
I occasionally use another one that is based on the following terminology.

I think of Objective-C objects as providing chip-level objects on top of
a C substrate that has always provided block-level objects (subroutines)
and gate-level objects (macros).

Thus when designing something, I think as a hardware designer might,
at each step thinking about whether this might be best packaged as
a gate-level object, a block-level object, a chip-level object, etc.

For example, when building a time-critical Queue class, I decided
that for clients for whom enqueing/dequeueing might be time-critical,
a block-level (subroutine-based) interface should be provided in
addition to the conventional chip-level (message-based) interface
for clients for whom purity and polymorphism were preferable. So
my Queue class looks like this:

@interface Queue:ListBasedCollection
...
-enqueue:aQueueMember 
{
	return (id)enqueue((QUEUE*)self, (MEMBER*)aQueueMember); 
}
@end
EXPORT QUEUE* enqueue(QUEUE* aQueue; MEMBER* aQueueMember) { ... }

In other words, the enqueue() subroutine corresponds to a chip
designer who decides to package the chip's functionality, not
only as a chip, but also as a resuable cel. Clients outside
his own company would use the functionality through the chip-level
interface. By packaging his work not only as a chip, but also as
a block (a cel) would allow experts within his company to reuse
the functionality through the lower-level, more complicated,
more efficient, less pluggable block-level interface.
-- 

Brad Cox; cox@stepstone.com; CI$ 71230,647; 203 426 1875
The Stepstone Corporation; 75 Glen Road; Sandy Hook CT 06482

andyk@kermit.UUCP (Andy Klapper) (10/30/90)

In article <129@contel0> barton@contel0.UUCP (Matthew Barton - R+D Software eng) writes:
>Goal: Time critical code should perform as well as C code, while retaining the 
>advantages of object-oriented design.
>
>Coding guidelines:
>
>2. Don't use inherited methods. They cannot be statically bound. 
>Inherited data is ok.

NOT TRUE !!  Inherited methods can be statically bound just like methods
defined in the class that calls it ! The following code fragment when compiled
will show this.

file -> One.h
#import "Object.h"
@interface One : Object
{}
- aOneMethod;
@end

file -> Two.h
#import "One.h"
@interface Two : One
{}
- testMethod;
@end

file -> Two.m
#import "Two.h"
@implementation Two
- testMethod
{
    [(Two *) self aOneMethod];   // Note: self must be cast to a type for
                                 // static binding to work.  This is VERY
                                 // dangerous as a sub class that over rides
                                 // the aOneMethod would not work as expected.
}
@end

>3. Don't nest method invocations, since the intermediate results are of 
>type "id". Explicitly declare the intermediate variables.
>	Example: replace this:
>		[vo_class2 m_status:[vo_class1 m_status]];
>		
>		with
>		
>		some_value = [vo_class1 m_status];
>		[vo_class2 m_status:some_value];
>

Also NOT TRUE !!!  as long as the type of vo_class1 is known, and the sBind
switch is set this will work.  Add the following lines to the above example
to show that this will work as well.

file -> One.h
add the method prototype 
- createOne;
file -> Two.m
add to testMethod the line
  [[(Two *) self createOne] aOneMethod];

NOTE: Both of these examples were run on a Sun 3 under Sun OS 4.0.3 with
the current released version of Objective-C.  If anybody would like the
full examples I can Email them to you.

>4. Avoid Stepstone library objects such as String, Collection, etc, 
>since these do not follow the guidelines given above. 
>Code your own objects, and re-use them.
>
    The ICpak101 libraries were designed to be generalized reusable
components.  When you get to the optimization phase you should optimize
the methods that will help you the most to optimize.  For example Stepstone
created a new Set class (HashSet) as part of ICpak201 because the performance
of a general purpose Set class was not good enough.

>
>This kind of coding raises all kinds of questions:
>	Could Stepstone provide an alternate version of runtime and ICpak101
>	libraries, which was optimized for speed?

    We could, but then we should offer several different versions of each
class.  Each version would have to be optimized for a different set of
conditions.  For example Stepstone should then provide different optimized
versions of osort (one optimized for the case of random data, one optimized
for data that is already mostly sorted ...).  The point being that
algorithmic changes get you more bang for the buck, but tend to be VERY 
application specific.

>	
>	Could static binding be improved to recognize the type of the receiver
>	just from a cast, rather than the data declaration? 

    This is already the case.  In a future release the compiler will also 
allow for static binding to class objects and to super inside of class methods.

>       Can the
>	inherited methods be static bound, in code where static (compile-time)
>	analysis shows that there is no method over-ride done by 
>	the sub-classes?

    Given the way that static binding is implemented in Objective-C, where the
method is defined is of no concern.  

>
>	Does anyone have any other suggestions for speed improvement,
>	which are specific to the Objective-C language (as opposed to
>	just general-purpose good-programming practices)?

    I am currently working on a new section/appendix for the Objective-C
manual that deals with optimizing Objective-C code for speed.  (I may be
able to send you a working copy if you want (I'd have to check first)).
Here are a couple things that I think you missed.
    1)  The 'methodFor:' method provides a way of getting an implementation
(function) pointer for an object, selector pair at runtime.  The 'methodFor:'
method allows for a little more flexibility in your code.  For example this
mechanism allows you to define a uniform collection that is optimized with 
the knowledge that all of it's elements are of the same time.  Static binding
will only allow you to define a collection that contains all objects of a
specific type because the type of the receiver has to be determined at 
compile time.
    2)  You have not mentioned anything about memory allocation and freeing.
Alot of time can be spent allocating and freeing objects in an Objective-C
program.  You may want to cache some objects for reuse or use objects 
allocated off of the stack.  When allocating objects off of the stack there
are a couple of things that you ought to know.  One, the data in an object
allocated off of the stack is NOT initialized.  The user is responsible for
initializing the data themselves (- initialize is the most common way).  Two,
the current release of the compiler does not properly set an attribute bit
that prevents the free method from trying to call 'free()' to free the memory
used by the object.  (The next release will)  What this means is that if the
initialization routine creates any additional objects you will have to create
a new free method that will release them, but not self.

>
>On un-related issues:

  I will pass you suggestions on.

>
>	matt


-- 
The Stepstone Corporation                    Andy Klapper
75 Glen Rd.                                  andyk@stepstone.com
Sandy Hook, CT 06482                         uunet!stpstn!andyk
(203) 426-1875    fax (203)270-0106