Obj-C optimization woes

Member
Posts: 28
Joined: 2005.04
Post: #1
I am working on a routine to calculate averaged normals. I am pretty much using a c++ tutorial as my guide and tossing it write into one of my obj-c objects, so I rewrote it in obj-c. I thought this would be trivial but when the original is run it takes < 1 second to run thru the routine and calc all the normals, but my obj-c version takes over 30 seconds to run. Rather unacceptable if you ask me.

Anyone have any thoughts on this?

Code:
int i, j, shared = 0;

for ( i = 0; i < [vertices count]; i++ ) {
       sum = [[WFVector alloc] init];
    for ( j = 0; j < [indexVerts count]; j++ ) {
        // Check if the vertex is shared
        if ( [[[indexVerts objectAtIndex:j] objectAtIndex:0] intValue] == i ||
         [[[indexVerts objectAtIndex:j] objectAtIndex:1] intValue] == i ||
         [[[indexVerts objectAtIndex:j] objectAtIndex:2] intValue] == i ) {
        [sum addVector:[tempNormals objectAtIndex:j]];
        shared++;
        }
    }
    [sum divideByScalar:(float)-shared];
    [sum normalize];
    [normals addObject: sum];
    shared = 0;
    [sum release];
  }


Could this have anything to do with alloc/init'ing an object and releasing it constantly. My init function for that object just sets 3 floats to 0. I am missing something very very basic here.

Regards
Quote this message in a reply
Member
Posts: 509
Joined: 2002.05
Post: #2
I don't know much about the inner workings of cocoa for allocation and init, but here are some small things I noticed that are probably trivial (unless you are calculating each frame)

You don't need to make the call [vertices count] every time you illiterate your loop, try changing

for ( i = 0; i < [vertices count]; i++ ) {

to

int max = [vertices count];
for ( i = 0; i <max; i++ ) {

Same thing for the other loop.

Try using normal function calls instead of object methods for stuff like [sum normalize]

EDIT : NSArray has got to be slower than plain C arrays.
Quote this message in a reply
Member
Posts: 28
Joined: 2005.04
Post: #3
Jake Wrote:You don't need to make the call [vertices count] every time you illiterate your loop, try changing ...
Thanks, I should be doing this in many places, I just took the ease of NSArrays forgranted. Although this did not speed up the normal calculation routine any noticable amount =\
Jake Wrote:Try using normal function calls instead of object methods for stuff like [sum normalize]
Not sure what you mean?
Jake Wrote:EDIT : NSArray has got to be slower than plain C arrays.
Most definelty a NSArray will be slower. I would be very suprised if it was this much slower though. These arrays contain Vectors which are objects as well, so I thought using all NSArrays would make it much easier. I will have to rework a huge portion of my code if the NSArrays are truly the problem. I highly doubt / hope it is not the problem.
Quote this message in a reply
Member
Posts: 201
Joined: 2002.06
Post: #4
I would not recommend using Obj-C classes for "tiny" classes like vectors, as there is some overhead when sending messages to Obj-C objects. I recommend either Obj-C++ so you can use C++ objects for "tiny" classes (ugly and painfully long compile times) or using some basic C structs for your vectors, etc. and manipulating them with C functions instead of Obj-C messages.
Quote this message in a reply
nabobnick
Unregistered
 
Post: #5
Without at least a little bit of timing of what inside the loop is taking the longest we'll never be able to help you (or for you to help yourself). Any of the method calls made inside your loop could be slowing it down. Finding out which of them is taking a lot of time is the secret to tuning your performance. See if you can use Shark to pinpoint which part of the loop is the slowest then concentrate there.
Quote this message in a reply
Member
Posts: 28
Joined: 2005.04
Post: #6
I did a Time Profile with shark and sampled from when this is running, and the biggest culprit is objc_msgSend at 23.3% of the load. Just as you all have suspected. I had no idea that objc method calls could be that expensive. Granted those loops are run depending on the mesh anywhere from 4 to 4000 times.

I guess the only place I can go is to alter my vector object. I would like to keep it as easy to use as possible, would going the ObjC++ route or the c structs route be best? Ease of use is what I am going for.

I have just learned ObjC a few months ago and when I started on this project I got a little over zealous with the ObjC object creation Blush . I learned my lesson.

Thanks for the fast replies!
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #7
One thing you can do to speed up Objective-C messages is to get an IMP for the method you want to use. See NSObject's -instanceMethodForSelector:. This allows you to call the method by using a function pointer rather than objc_msgSend, and should give you a nice speed boost in tight loops like this.

- Alex Diener
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #8
Part of the problem, as I see it, is reduntant lookup of the same object three times for each iteration of the inner loop.

NitroPye Wrote:
Code:
...snip
        if ( [[[indexVerts objectAtIndex:j] objectAtIndex:0] intValue] == i ||
         [[[indexVerts objectAtIndex:j] objectAtIndex:1] intValue] == i ||
         [[[indexVerts objectAtIndex:j] objectAtIndex:2] intValue] == i ) {

...snip

How about something more like:

Code:
id vert = [indexVerts objectAtIndex:j];
if ( [[vert objectAtIndex: 0] intValue] == i ||
     [[vert objectAtIndex: 1] intValue] == i ||
     [[vert objectAtIndex: 2] intValue] == i )

That's a little better. But, frankly, if I were you I'd either let my vertices be a struct, with C functions to implement normalization and other vector ops, or I'd go the c++ route and make a vector class with operator overloading ( this is what I do, but I recognize that many people don't like C++ ).

Anyway, you've *got* to not treat vertices as generic NSArrays. That's WAY too generic: go with a struct, or a float [4], or a union overlapping the two...
Quote this message in a reply
Member
Posts: 28
Joined: 2005.04
Post: #9
ThemsAllTook Wrote:One thing you can do to speed up Objective-C messages is to get an IMP for the method you want to use. See NSObject's -instanceMethodForSelector:. This allows you to call the method by using a function pointer rather than objc_msgSend, and should give you a nice speed boost in tight loops like this.
I will more then likely move over to simple c functions to do my bidding , but in the mean time I would like to trudge onward with my development, and your idea of using an IMP for method calls keeps me from having to rip out large portions of code.

I am just having a problem wrapping my head around using an IMP.

Lets say I have this:

Code:
int i;
for (i = 0; i < 5000; i++) {
     object == [[OBJECT alloc init];
     [object doSomething];
     [object againSomethingWith: this];
     [object doSomethingWithInt: i];
     [object release];
}

How would I go about doing this with an IMP for each method call?
So far I have only been able to figure out the simple first -doSomething selector, and even then I am not sure it will by-pass the obj-c overhead. And even still I am not sure how to use it. Pointers to functions are scarey in my book.

Code:
IMP doSomethingFast = [object instanceMethodForSelector:@selector(doSomething)];
    doSomethingFast( ??? );
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #10
Here's the way I use IMPs:

Code:
/* Example method: - (int) doSomething: (int) something; */

/* Function pointer variable that holds the IMP */
int (* doSomethingIMP)(id, SEL, int);

/* Getting the IMP */
(IMP) doSomethingIMP = [MyClass instanceMethodForSelector: @selector(doSomething:)];

/* Calling the IMP */
(*doSomethingIMP)(myObject, @selector(doSomething:), someParameter);

The first two arguments to an IMP are the object itself, and the selector of the method you're calling. After that, zero or more arguments depending on how many parameters the method takes. Hope that helps.

- Alex Diener
Quote this message in a reply
Member
Posts: 28
Joined: 2005.04
Post: #11
Thank you so much for the responces everyone. I was able to get it to run at a MUCH faster time.

I am still learning how to properly understand and use Shark for optimization but I was able to see that it wasn't really the objc_msgSend calls that were the problem. It was the fact I put a bunch of NSNumbers into a NSMutableArray, because every face can have as many points as it wants. But for calculating the normals I do not need every point, just three, so I created an array of size numFaces * three, and filled it with the first three points of every face.

The time it took to run this method on a mesh with 3000 verts went from ~25-30 seconds to less then a second.

Here is the obligitory, what I was able to create with your help picture:

[Image: objLoader.png]
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Random Optimization Questions wyrmmage 32 8,739 Jun 12, 2007 05:49 PM
Last Post: akb825