GCD Poor Performance GCD Poor Performance objective-c objective-c

GCD Poor Performance


Is there a reason you're not using the GCD C API and the dispatch_* family of functions? You don't have much control over the GCD aspects of NSOperationQueue (like which queue you want to submit the blocks to). Also, I can't tell if you're using iOS or not, but NSOperationQueue does not use GCD on iOS. That might be the reason it spawned so many threads. Either way, your code will be shorter and simpler if you use the GCD API directly:

- (double) detectCollisionsInArray:(NSArray*)objects{     int count = [objects count];  if (count > 0)  {    double time = CFAbsoluteTimeGetCurrent();    dispatch_group_t group = dispatch_group_create();    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    for (int i = 0; i < count; i++)    {      dispatch_group_async(group, queue, ^{        for (int j = i + 1; j < count; j++)        {          dispatch_group_async(group, queue, ^{            /** LOTS AND LOTS OF WORK FOR EACH OBJECT **/          });        }      });    }    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);    dispatch_release(group);    return CFAbsoluteTimeGetCurrent() - time;  }  return 0;}

You can use a dispatch_group to group all of the executions together and wait for them all to finish with dispatch_group_wait. If you don't care to know when the the blocks finish, you can ignore the group part and just use dispatch_async. The dispatch_get_global_queue function will get one of the 3 concurrent queues (low, default or high priority) for you to submit your blocks to. You shouldn't have to worry about limiting the thread count or anything like that. The GCD scheduler is supposed to do all of that for you. Just make sure you submit to a concurrent queue, which could either be one of the 3 global queues, or a queue you've created by passing DISPATCH_QUEUE_CONCURRENT to dispatch_queue_create (this is available starting OS X 10.7 and iOS 5.0).

If you're doing some file I/O in each block or taxing some other resource, you might need to reign in GCD and limit the number of blocks you're submitting to the queue at once. This will have the same effect as limiting the concurrent operation count in an NSOperationQueue. You can use a GCD semaphore to do this:

- (double) detectCollisionsInArray:(NSArray*)objects{     int count = [objects count];  if (count > 0)  {    double time = CFAbsoluteTimeGetCurrent();    dispatch_group_t group = dispatch_group_create();    dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    for (int i = 0; i < count; i++)    {      dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);      dispatch_group_async(group, queue, ^{        for (int j = i + 1; j < count; j++)        {          dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);          dispatch_group_async(group, queue, ^{            /** LOTS AND LOTS OF WORK FOR EACH OBJECT **/            dispatch_semaphore_signal(semaphore);          });        }        dispatch_semaphore_signal(semaphore);      });    }    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);    dispatch_release(group);    dispatch_release(semaphore);    return CFAbsoluteTimeGetCurrent() - time;  }  return 0;}

Once you get the hang of it, GCD is very simple to use. I use it all over my code now.

Can anyone help to put me on the right track and perhaps provide a link to a good GCD tutorial?

Run, don't walk over to Mike Ash's blog. His series on GCD is the clearest and most concise I've seen, and it'll only take you around 30 minutes to read the whole thing. Apple's WWDC videos from 2010 on GCD And blocks are also pretty good.


In your code you are delaying the work you need to do for each object until the end of the nested for loop. That said, when the loop finishes you will have one operation with lots of blocks for a bunch of objects and you won't thereby take advantage of the GCD properly.

I would suggest you create one NSBlockOperation for each object and add it to the NSOperationQueue in the end of each for (int j = i + 1; j < count; j++) iteration.

This way, the system will begin processing the work you need to do for each object as soon as the iteration ends.

Also keep in mind that the queue shouldn't be much larger than the available processors, otherwise you will have some overhead on the thread switch process that will compromise speed.