As you may remember, I am trying to use GCD to speed up some of my code, namely a collision detection and resolution engine. However, I am clearly doing something wrong because all of my GCD code is significantly slower and less consistent than my serial code (between 1.4x and 10x slower). Allow me to give you an example: I am iterating over an array in a bubble-sort fashion to determine all possible collisions among objects in that array:
- (double) detectCollisionsInArray:(NSArray*)objects
{
int count = [objects count];
if (count > 0)
{
double time = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < count; i++)
{
for (int j = i + 1; j < count; j++)
{
/** LOTS AND LOTS OF WORK FOR EACH OBJECT **/
}
}
return CFAbsoluteTimeGetCurrent() - time;
}
return 0;
}
Pretty straightforward, and it seems to perform well given the constraints of the problem. However, I would like to take advantage of the fact that the state of each object is not modified in the code section and use GCD to parallelize this work. To do this I am trying something like this:
- (double) detectCollisionsInArray:(NSArray*)objects
{
int count = [objects count];
if (count > 0)
{
NSOperationQueue* opQueue = [[NSOperationQueue alloc] init];
NSBlockOperation* blockOperation = nil;
double time = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < count; i++)
{
for (int j = i + 1; j < count; j++)
{
void (^workBlock) (void) = ^()
{
/** LOTS AND LOTS OF WORK FOR EACH OBJECT **/
};
if (!blockOperation)
{
blockOperation = [NSBlockOperation blockOperationWithBlock:b];
}
else
{
[blockOperation addExecutionBlock:workBlock];
}
}
}
[opQueue addOperation:blockOperation];
[opQueue autorelease];
return CFAbsoluteTimeGetCurrent() - time;
}
return 0;
}
Can anyone help to put me on the right track and perhaps provide a link to a good GCD tutorial? I have looked over several GCD tutorials and scoured all of the documentation and I still feel that my grasp on the subject is tenuous at best. Thanks!
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.
Related
I'm new to working with concurrency in Objective-C. I've taken look at some demo codes of concurrency programming.
I want to print natural numbers sequentially but in 2 different threads. Like:
1: Thread-XX
2: Thread-YY
3: Thread-XX
4: Thread-YY
5: Thread-XX
6: Thread-YY
.....................
.....................
and so on. But I can't figure out how to do that. Any help?
We can show you how to do this, but it's not a very useful scenario. You're going to spent a lot of time coordinating these two threads with semaphores or the like, largely diminishing the value of having stuff running on multiple threads. Often when you write multithreaded code, you want to maximize concurrency by minimizing the amount of time spent waiting for some signal from some other thread. This attempt to alternate between these two threads is antithetical to our general intent of multithreaded programming.
That having been said, it could look a bit like:
dispatch_queue_t xx = dispatch_queue_create("xx", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t yy = dispatch_queue_create("yy", DISPATCH_QUEUE_SERIAL);
dispatch_semaphore_t semaphoreXX = dispatch_semaphore_create(0);
dispatch_semaphore_t semaphoreYY = dispatch_semaphore_create(1); // seed this with one extra signal from YY
NSInteger __block value = 0;
dispatch_async(xx, ^{
for (NSInteger i = 0; i < 100; i++) {
dispatch_semaphore_wait(semaphoreYY, DISPATCH_TIME_FOREVER); // wait for signal from YY
value += 1;
NSLog(#"%ld: xx", (long)value);
[NSThread sleepForTimeInterval:0.1]; // 1/10 second delay so we can see what's going on
dispatch_semaphore_signal(semaphoreXX); // send signal from XX
}
});
dispatch_async(yy, ^{
for (NSInteger i = 0; i < 100; i++) {
dispatch_semaphore_wait(semaphoreXX, DISPATCH_TIME_FOREVER); // wait for signal from XX
value += 1;
NSLog(#"%ld: yy", (long)value);
[NSThread sleepForTimeInterval:0.5]; // 1/2 second delay so we can see what's going on
dispatch_semaphore_signal(semaphoreYY); // send signal from YY
}
});
Usually when we're updating one variable from multiple threads, we'd synchronize our access to that object, but this "have the threads alternate" logic eliminates our need to do that, in this particular case.
I have two arrays: array1 and array2. Each object of arrays is an array too (2D arrays). In this way I multiple them. So how I have big arrays I use dispatch_apply. Every time i receive different results include a right result. Maybe somebody knows how to fix it?
dispatch_apply([array2 count], queue, ^(size_t j)
{
k = 0;
for (int l = 0; l < [[array1 objectAtIndex:0] count]; l++) {
k += [[[array1 objectAtIndex:i] objectAtIndex:l] intValue] *
[[[array2 objectAtIndex:j] objectAtIndex:l] intValue];
}
kNSNumber = [NSNumber numberWithInt:k];
[multipliedArrayInto replaceObjectAtIndex:j withObject:kNSNumber];
});
[resulArray insertObject:multipliedArrayInto atIndex:i];
}
There's two things, I can suggest, and I bet one of them (or both) is the overarching solution to your problem.
First, I would declare k local to the block, so there would be no question that you are overwriting it or not. You likely have the same problem with kNSNumber inside the block. If you are just using that NSNumber instance to slam into the multipliedArrayInto accumulator, you may as well remove kNSNumber, and use #(k) in it's place (if only to be more readable). Similarly, make sure multipliedArrayInto is declared just before the dispatch_apply, in what looks like an outer for loop (where ever i is coming from). And finally, make sure resulArray is instantiated, or otherwise readied just before that outer for loop.
Second, is queue a concurrent or serial queue? If you are using dispatch_apply like a parallel-executing for/enumeration -- which is likely, I think, so you are taking about handling "big arrays" efficiently -- then you are practically guaranteeing that k is being overwritten. If you change it to serial, it may work as designed. If you want it to be parallel, you will need to move the declaration of your k accumulator inside the block, and make sure the declaration of other variables makes sense, too.
Update to reflect question updates:
#antonytonies ideally, your followup answer on this thread should be moved into the question itself, so that people can follow this thread easier.
So, it looks like what I described is exactly your problem.
The global queues are all concurrent queues, which means that (hypothetically) all the dispatch blocks are executing at once, and the contents of k and other variables are getting blown away depending on how the order of the blocks executes.
I've taken your update (in the "answer" you added), and modified it to probably work:
// I renamed your method, because nameless parameters pain me. This is cosmetic, and doesn't
// matter for the problem at hand.
- (NSMutableArray *)multiplicationArrays:(NSMutableArray *)array vector:(NSMutableArray *)vector
{
// IMHO, you want to set resultArray to nil here. Another option is to set it to nil in the
// else case, below. Properties in Objective-C are initalized to nil,0,false,etc; you can
// rely on ARC to initialize pointer to objc objects on the stack, too. However, someone
// reading this code may or may not know that. IMHO, using the explicitly assignement makes it
// clear that you're going to be returning `nil` or an instance of `NSMutableArray`.
NSMutableArray *resultArray = nil;
if ([[array objectAtIndex:0] count] == [vector count]) {
// Nicely done w/ pre-allocating the result array here, so that there's no question
// of the indexes matches the results later on.
resultArray = [[NSMutableArray alloc] initWithCapacity:[array count]];
for (int i=0; i < [array count]; i++) {
[resultArray insertObject:[NSNull null] atIndex:i];
}
// 'queue' here is a concurrent queue. This means that you are proclaiming to the runtime
// that the blocks being executed are able to operate correctly w/o interference from each
// other. This is also thought of in terms of parallel execution: all these blocks may run
// **at once**. This *also* means, that you must not share storage between them.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply([array count], queue, ^(size_t j) {
// Moved 'result' inside the block.
NSInteger result = 0;
for (int l = 0; l < [[array objectAtIndex:0] count]; l++) {
// These array reads are **NOT** thread safe. They probably don't cause must trouble in
// practice, but you may want to reconfigure this.
result += [[[array objectAtIndex:j] objectAtIndex:l] intValue] * [[vector objectAtIndex:l] intValue];
}
// The replace of the object into resultArray is **NOT** thread-safe.
// This probably hasn't caused you much trouble, since you can guarantee that
// you aren't writing at the same index. However, I would strongly suggest to
// change this to be thread-safe.
[resultArray replaceObjectAtIndex:j withObject:#(result)];
});
}
else {
NSLog(#"matrix count isn't correspond");
}
return resultArray;
}
Finally: consider just using Apple's Accelerate framework for this sort of problem solving. It's available on OSX and iOS, so you should have all of your bases covered.
it's the same thing if I multiple 2D-array and vector
-(NSMutableArray*)multiplicationArraysWithVector:(NSMutableArray *)array :(NSMutableArray *)vector
{
NSMutableArray* resultArray;
if ([[array objectAtIndex:0] count] == [vector count])
{
resultArray = [[NSMutableArray alloc] initWithCapacity:[array count]];
for (int i=0; i < [array count]; i++) {
[resultArray insertObject:[NSNull null] atIndex:i];
}
__block NSInteger result;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply([array count], queue, ^(size_t j)
{
result = 0;
for (int l = 0; l < [[array objectAtIndex:0] count]; l++) {
result += [[[array objectAtIndex:j] objectAtIndex:l] intValue] * [[vector objectAtIndex:l]intValue];
}
[resultArray replaceObjectAtIndex:j withObject:#(result)];
});
}
else
{
NSLog(#"matrix count isn't correspond");
}
return resultArray;
}
In this case I can get a right or wrong data result.
Can someone help,
I have a NSMutablearray which contains approx a 1000 data entries.
What i want to do is go though these entries one by one and send them to a server with HTTP request.
However its important that i only send them one at a time, so i have the following pseudo code
for(int i =0; i < array.count; i++)
{
[ServerLayer ServerUploadRow:[array objectAtIndex:i] : ^void (int ReturnValue, BOOL err){
if(err == true)
{
//Do some local stuff here.
}
}];
}
what i ideally want to do is having something like the following
for(int i =0; i < array.count; i++)
{
//Wait here till i know that we not waiting on a block to complete
[ServerLayer ServerUploadRow:[array objectAtIndex:i] : ^void (int ReturnValue, BOOL err){
if(err == true)
{
//Do some local stuff here.
//Signify that we done
}
}];
}
the function ServerUploadRow just sends HTTP command.
Thanks
Assuming that ServerUploadRow runs asynchronously, you can have the request initiate the next request in the completion block of the previous one:
- (void)initiateRequestNumber:(NSInteger)index
{
[ServerLayer ServerUploadRow:array[index] : ^(int ReturnValue, BOOL err){
if (err == true) {
//Do some local stuff here.
} else {
NSInteger nextIndex = index + 1;
if (nextIndex < array.count) {
[self initiateRequestNumber:nextIndex];
} else {
// do whatever you want now that everything is done
}
}
}];
}
And you'd start this with:
[self initiateRequestNumber:0];
If you can issue these requests concurrently, or better combine all of this in one request, it will be much faster. I'd suggest considering refactoring your web service to enable this, if at all possible. But submitting 1000 network requests sequentially is going to horribly slow (as you'll suffer network latency 1000 times).
I'm writing a Cocoa app that does sometimes long calculations. I'd like to be able to abort those calculations with a cmd-key sequence. I start the calculations from a command line that is implemented in a subclass of NSTextView. I've over ridden keyDown and get events there, but only when the calculation finishes. I can't figure out how to have the long calculation force an event check periodically that would cause keyDown to be called so I can set a flag to abort the calculation. Seems like there may be an easy way, but I can't seem to find it.
Without worrying about background threads, I was hoping for something like this:
//commandView is a subclass of NSTextView
commandView* theCommands;
extern int continueCalc;
#implementation commandView
- (void)keyDown:(NSEvent *)anEvent{
static int first = 1;
if(first){
theCommands = self;
first = 0;
}
NSString *theKey = [anEvent characters];
[super keyDown:anEvent];
if([theKey isEqualToString:#"s"]){
NSLog(#"start 10 second calc");
continueCalc = 1;
doCalc(10);
} else if ([theKey isEqualToString:#"x"]){
NSLog(#"stop calc");
continueCalc = 0;
}
}
- (void)checkEvent{
NSLog(#"would like to force an event check so that typing x would stop the calc");
}
#end
// and the separate calculation code in another file:
int continueCalc = 1;
extern commandView* theCommands;
void doCalc(int n){
clock_t start;
for (int i=0; i<n && continueCalc; i++) {
start = clock();
while ( (clock()- start)*60/CLOCKS_PER_SEC < 60); //wait a second
// something here to check an event
[theCommands checkEvent];
}
}
One way to do this properly is with NSThread. You can very easily create a new thread which just calls your calculation method. In your calculation method, you check a variable to see if it's been aborted. Meanwhile on the main thread, the UI remains responsive and you can use an event (a key press or button press) to set the variable which is checked on the other thread. I might look something like this:
- (void)startCalculations
{
[NSThread detachNewThreadSelector:#selector (runCalculations)
toTarget:myObject
withObject:nil]; // Or you can send an object if you need to
}
Then in the code for myObject:
- (void)runCalculations
{
for (int i = 0; (i < maxWhatever) && (!stopCalculations); i++)
{
... do one iteration of your calculation
}
}
Then when your UI code gets the proper key or button press, you simply tell myObject to set stopCalculations to YES.
[myObject setStopCalculations:YES];
I should note that there are other ways to do this, like using GCD or pthreads directly, but this is a very simple way to just run a single method on another thread, and all the details of starting and tearing down the thread are taken care of for you.
The answer above works perfectly and showed me how to approach threads, which I hadn't used before. Thanks for that. With that working, I wanted the calculation to be able to report progress back to the command decoder (NSTextView). Doing that in the new thread caused problems, so I looked into using GCD. The implementation below seems to work, but this is my first GCD code and I would welcome any suggestions or caveats.
// CommandView is a subclass of NSTextView
commandView* theCommands;
extern int continueCalc;
#implementation commandView
- (void)keyDown:(NSEvent *)anEvent{
static int first = 1;
if(first){
theCommands = self;
}
NSString *theKey = [anEvent characters];
if([theKey isEqualToString:#"s"]){
[self appendText:#"Start\n"];
[self startCalculations];
return;
} else if ([theKey isEqualToString:#"x"]){
[self appendText:#"\nStop"];
continueCalc = 0;
return;
}
[super keyDown:anEvent];
}
- (void)startCalculations
{
void doCalc(int);
continueCalc = 1;
dispatch_queue_t queue = dispatch_queue_create("oma.oma2.CommandTask",0);
dispatch_async(queue,^{
for (int i=0; i<10 && continueCalc; i++) {
doCalc(0);
NSLog(#"%d",i);
}
});
}
-(void) appendText:(NSString *) string{
[self.textStorage.mutableString appendString:string];
}
#end
// the separate calculation code in a different file
#import "commandView.h"
extern commandView* theCommands;
int continueCalc = 1;
void doCalc(int n){
clock_t start;
start = clock();
while ( (clock()- start)*60/CLOCKS_PER_SEC < 60); //wait a second
// print progress in the main thread
dispatch_sync(dispatch_get_main_queue(),^{[theCommands appendText:#"."];});
}
Didn't wanna just post, "Hey, guys, can you come up with an awesome randomization method for me?" so I've been studying algorithms for shuffling a deck of cards, creating truly random permutations, etc., ALL DAY LONG and it's been rrrrrrrreally daunting. Finally settled on a simpler approach, shown below. While I do want to create a quality game, of course, I'm not going to be using this algorithm in an online casino or anything. It's just an iOS game. So, is this random/memory-efficient enough? Or should I be giving this even MORE time and effort? TIA
Aside: Whenever I write "TIA," I think, "This is Africa," not "Thanks in advance."
#implementation NSMutableArray (Shuffle)
-(void)shuffle
{
for (int i = [self count] - 1; i > 0; i--) {
[self exchangeObjectAtIndex:(arc4random() % ([self count] - 1))
withObjectAtIndex:i];
}
}
#end
EDIT:
Ok. Wanted to run some test code so I don't post something dumb. :) In response to Adam and Nielsbot, you're recommending something more like this?
-(void)shuffle
{
for (int i = [self count] - 1; i > 0; i--) {
[self exchangeObjectAtIndex:(arc4random_uniform([self count] - 1))
withObjectAtIndex:i];
}
}
Is that right?
Would there be any benefit to repeating it, like so?
-(void)shuffle
{
for (int i = [self count] - 1; i > 0; i--) {
[self exchangeObjectAtIndex:(arc4random_uniform([self count] - 1))
withObjectAtIndex:i];
}
for (int i = [self count] - 1; i > 0; i--) {
[self exchangeObjectAtIndex:(arc4random_uniform([self count] - 1))
withObjectAtIndex:i];
}
}
That's almost the so called Fisher-Yates algorithm, it's O(n) as time complexity and memory efficience is not considerable since you don't allocate anything.
The only thing I'd change is the randomly chosen index that would be in range [0, i] for i-th iteration instead that the whole range (to avoid just swapping two elements twice), then it would become the algorithm I linked.