Re: Objective-C question
Re: Objective-C question
- Subject: Re: Objective-C question
- From: "Louis C. Sacha" <email@hidden>
- Date: Wed, 5 May 2004 17:54:24 -0700
Hello...
Just to inject a little reality into this -- your results don't
reflect the actual results someone should expect by accessing iVars
of a class directly instead of through accessors (note: it is NOT a
good idea to bypass accessors for ivars that are pointers to objects,
and doing so even with values makes significant assumptions about
the implementation of the class). While I agree that there are
situations where it is beneficial to access values stored in an
instance variable directly, your numbers sound like manufacturer
benchmarks :)
First, the test just measures the raw read/write access to the
variable. This is equivalent to saying that given the speed that data
can be read from the hard drive, it should be possible to convert an
hour of DV video to mpeg2 in about 15 minutes, as opposed to the 7 or
so hours it takes when I'm running bitvice on a 450Mhz G4 Cube. Even
if I had a faster hard drive, it wouldn't make a significant amount
of difference in the total time. Anything else that you do
(calculations, etc...) will add to the time spent on each iteration,
and as the amount of additional processing increases, the effect of
the difference in speed from the read/write of variables decreases.
Second, it is very unlikely that a situation where this kind of
access is required would require such a small memory footprint. It is
far more likely that a large number of records/objects will be
accessed, and this implies a larger effect on performance due to
memory management.
A modified version of your test is at the end of this message, that
shows some of these effects in action. I used an array of objects
instead of just a single object, ran a number of tests varying the
array size and the number of loops (although the total number of
get/set iterations was kept constant at 100 million).
/*
I used -Os for these tests... on a dual 800 Mhz G4 w/ 1.5 GB RAM
model size is the array size, loopCount is the number of external loops
I had to retype these into the email, since I couldn't copy/paste
since it was run on a different computer. So, it's possible there's a
typo, even though I double checked them.
The faster result for tDirect with modelSize=100 compared to
modelSize=1 is accurate.
Each line is the result of a seperate test run, with the indicated
values used to modelSize and loopCount.
modelSize = 1; loopCount = 100000000; tDirect = 1.14s, tAccessors =
13.56s (100000000 iterations)...
modelSize = 100; loopCount = 1000000; tDirect = 1.02s, tAccessors =
14.01s (100000000 iterations)...
modelSize = 10000; loopCount = 10000; tDirect = 2.45s, tAccessors =
15.74s (100000000 iterations)...
modelSize = 100000; loopCount = 1000; tDirect = 8.67s, tAccessors =
23.82s (100000000 iterations)...
modelSize = 1000000; loopCount = 100; tDirect = 15.42s, tAccessors =
28.05s (100000000 iterations)...
modelSize = 10000000; loopCount = 10; test has exited due to signal
11 (SIGSEGV). :)
*/
My modified version still doesn't do anything with the values other
than setting them and getting them. The difference in these results
is caused by the overhead from simply accessing the objects/structs
from a standard C array (not an NSArray) and the necessary memory
management for a larger model. The difference between the two methods
is more or less constant, and if any real work was actually done in
the loop, the % difference in speed would drop even more
significantly.
Also, note that the roughly constant 13 second difference in the
total times is for 100 million set/get iterations. That's a lot of
iterations :) So even though 13 seconds sounds like a big difference,
if you drop the number of total iterations to 1 million, the
difference in total time is only 0.131s (from a test using modelSize=
1000; loopCount = 1000; with the output accuracy bumped up to 3
decimal places).
Anyway, the code follows... Louis
#import <Foundation/Foundation.h>
@interface MyClass : NSObject { @public int x; }
- (int) getX;
- (void) setX: (int) anInt;
@end
@implementation MyClass
- (int) getX {return x;}
- (void) setX: (int) anInt { x = anInt; }
@end
typedef struct { @defs(MyClass); } ClassStr;
int main(int argc, char **argv)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
unsigned modelSize = 1000000;
unsigned loopCount = 100;
double t1, t2;
NSDate *startDate;
NSLog(@"starting - building models");
MyClass *obj[modelSize];
ClassStr *cs[modelSize];
unsigned i;
for (i=0; i<modelSize; i++)
{
obj[i] = [[MyClass alloc] init];
cs[i] = (ClassStr *) obj[i];
}
NSLog(@"beginning tests");
unsigned j,k;
startDate = [NSDate date];
for(j=0; j<loopCount; j++)
{
for (k=0; k<modelSize; k++)
{
cs[k]->x = j;
}
for (k=0; k<modelSize; k++)
{
j = cs[k]->x;
}
}
t1 = - [startDate timeIntervalSinceNow];
startDate = [NSDate date];
for(j=0; j<loopCount; j++)
{
for (k=0; k<modelSize; k++)
{
[obj[k] setX: j];
}
for (k=0; k<modelSize; k++)
{
j = [obj[k] getX];
}
}
t2 = - [startDate timeIntervalSinceNow];
NSLog(@"tDirect =%.2fs, tAccessors = %.2fs (%d iterations)",
t1, t2, loopCount*modelSize);
[pool release];
return 0;
}
Classes give you much more than just encapsulation. They give you
all sorts of useful memory management features, convenient
inheritance of methods and a way to make your code more readable.
There are many good reasons for using these features and the other
benefits of classes even if you care nothing for encapsulation.
On the other hand it is worth noting that Objective-C classes are
not without cost. Consider the code for blah.m attached to the end
of this mail. It makes an object and then uses @defs() to make a C
structure with the same layout. Here's the output:
Direct took 0.68s, methods took 9.10s for 100000000 loops
Reading and writing the instance variable many times using direct
access records 6.8ns per loop on a 1.25GHz G4 whereas using methods
to get and set the same instance variable takes 91ns per loop,
thirteen times slower. There is clearly a large cost associated
with making the method calls themselves, and worse the disruption of
processor flow by having to make calls to objc_msgSend all the time
means that the compiler's optimisation phase is much less effective;
in this test I deliberately forced the compiler not to optimise
around these calls and removing the 'volatile' directive drops the
direct access loop to 1.8ns, 50 time faster than method access. In
many cases where dealing with large bodies of data the overhead of
using method access to instance variables is unacceptable.
Nicko
...
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.