How about iOS GCD Performance? - ios

I invoke a method( named A) repeatedly about 3000 times per seconds, in A method, I allocate some resources and do something.
If I allocate and do something in A method directly, it works fine. But if I allocate and do something in a customed serial thread created by GCD like blow, it will crash
NSString *queueName = [NSString stringWithFormat:#"com.realank.thread"];
dispatch_queue_t serialQueue = dispatch_queue_create([queueName cStringUsingEncoding:NSUTF8StringEncoding], NULL);
dispatch_async(initMidMenu, ^{
// alloc and do something
});
So I think why it will crash is because the performance limit about GCD thread, is anybody know about it? Thanks very much~

You're allocating a new dispatch queue when you call dispatch_queue_create. You must release it afterwards using dispatch_releaseas mentioned by Apple. Otherwise you'll run out of memory!!!
https://developer.apple.com/reference/dispatch/1453030-dispatch_queue_create#return-value
You can get a reference to the newly created queue by the return value of the dispatch_queue_create method, and then release it afterwards.
Here's how Apple has mentioned it in their documentation.
When your application no longer needs the dispatch queue, it should
release it with the dispatch_release function.

Related

Why is NSOperationQueue.mainQueue.maxConcurrentOperationCount set to 1

The reason for this question is because of the reactions to this question.
I realized the understanding of the problem was not fully there as well as the reason for the question in the first place. So I am trying to boil down the reason for the other question to this one at it's core.
First a little preface, and some history, I know NSOperation(Queue) existed before GCD, and and they were implemented using threads before dispatch queues.
The next thing is that you need to understand is that by default, meaning no "waiting" methods being use on operations or operation queues (just a standard "addOperation:"), an NSOperation's main method is executed on the underlying queue of the NSOperationQueue asynchronously (e.g. dispatch_async()).
To conclude my preface, I'm questioning the purpose of setting NSOperationQueue.mainQueue.maxConcurrentOperationCount to 1 in this day and age, now that the underlyingQueue is actually the main GCD serial queue (e.g. the return of dispatch_get_main_queue()).
If NSOperationQueue.mainQueue already executes it's operation's main methods serially, why worry about maxConcurrentOperationCount at all?
To see the issue of it being set to 1, please see the example in the referenced question.
It's set to 1 because there's no reason to set it to anything else, and it's probably slightly better to keep it set to 1 for at least three reasons I can think of.
Reason 1
Because NSOperationQueue.mainQueue's underlyingQueue is dispatch_get_main_queue(), which is serial, NSOperationQueue.mainQueue is effectively serial (it could never run more than a single block at a time, even if its maxConcurrentOperationCount were greater than 1).
We can check this by creating our own NSOperationQueue, putting a serial queue in its underlyingQueue target chain, and setting its maxConcurrentOperationCount to a large number.
Create a new project in Xcode using the macOS > Cocoa App template with language Objective-C. Replace the AppDelegate implementation with this:
#implementation AppDelegate {
dispatch_queue_t concurrentQueue;
dispatch_queue_t serialQueue;
NSOperationQueue *operationQueue;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
concurrentQueue = dispatch_queue_create("q", DISPATCH_QUEUE_CONCURRENT);
serialQueue = dispatch_queue_create("q2", nil);
operationQueue = [[NSOperationQueue alloc] init];
// concurrent queue targeting serial queue
//dispatch_set_target_queue(concurrentQueue, serialQueue);
//operationQueue.underlyingQueue = concurrentQueue;
// serial queue targeting concurrent queue
dispatch_set_target_queue(serialQueue, concurrentQueue);
operationQueue.underlyingQueue = serialQueue;
operationQueue.maxConcurrentOperationCount = 100;
for (int i = 0; i < 100; ++i) {
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(#"operation %d starting", i);
sleep(3);
NSLog(#"operation %d ending", i);
}];
[operationQueue addOperation:operation];
}
}
#end
If you run this, you'll see that operation 1 doesn't start until operation 0 has ended, even though I set operationQueue.maxConcurrentOperationCount to 100. This happens because there is a serial queue in the target chain of operationQueue.underlyingQueue. Thus operationQueue is effectively serial, even though its maxConcurrentOperationCount is not 1.
You can play with the code to try changing the structure of the target chain. You'll find that if there is a serial queue anywhere in that chain, only one operation runs at a time.
But if you set operationQueue.underlyingQueue = concurrentQueue, and do not set concurrentQueue's target to serialQueue, then you'll see that 64 operations run simultaneously. For operationQueue to run operations concurrently, the entire target chain starting with its underlyingQueue must be concurrent.
Since the main queue is always serial, NSOperationQueue.mainQueue is effectively always serial.
In fact, if you set NSOperationQueue.mainQueue.maxConcurrentOperationCount to anything but 1, it has no effect. If you print NSOperationQueue.mainQueue.maxConcurrentOperationCount after trying to change it, you'll find that it's still 1. I think it would be even better if the attempt to change it raised an assertion. Silently ignoring attempts to change it is more likely to lead to confusion.
Reason 2
NSOperationQueue submits up to maxConcurrentOperationCount blocks to its underlyingQueue simultaneously. Since the mainQueue.underlyingQueue is serial, only one of those blocks can run at a time. Once those blocks are submitted, it may be too late to use the -[NSOperation cancel] message to cancel the corresponding operations. I'm not sure; this is an implementation detail that I haven't fully explored. Anyway, if it is too late, that is unfortunate as it may lead to a waste of time and battery power.
Reason 3
As with mentioned with reason 2, NSOperationQueue submits up to maxConcurrentOperationCount blocks to its underlyingQueue simultaneously. Since mainQueue.underlyingQueue is serial, only one of those blocks can execute at a time. The other blocks, and any other resources the dispatch_queue_t uses to track them, must sit around idly, waiting for their turns to run. This is a waste of resources. Not a big waste, but a waste nonetheless. If mainQueue.maxConcurrentOperationCount is set to 1, it will only submit a single block to its underlyingQueue at a time, thus preventing GCD from allocating resources uselessly.

Can block capture a CoreFundation object?

In Apple's doc I can't find what I can do when I want to capture a CoreFoundation object.
But in Apple's Concurrency Programming Guide. It seems the sample code use some code when dispatch object is not support ARC just like this:
void average_async(int *data, size_t len, dispatch_queue_t queue, void (^block)(int))
{
// Retain the queue provided by the user to make
// sure it does not disappear before the completion
// block can be called.
dispatch_retain(queue);
// Do the work on the default concurrent queue and then
// call the user-provided block with the results.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
int avg = average(data, len);
dispatch_async(queue, ^{ block(avg);});
// Release the user-provided queue when done
dispatch_release(queue);
});
}
Do I need to use CFObject like the DispatchObject before. But if I need to invoke the block many times?
Maybe I can use __attribute__((NSObject)), but I don't know what will happen!
Does Apple say something about this?
I didn't see anything at Apple explicitly, but I do see some mentions in the llvm.org documentation, which I found elaborated on in this cocoa-dev mailing list thread.
It looks like you should be okay for using __attribute__((NSObject)), as it's given an implicit "__strong" qualification (from the documentation) and in a practical sense (from the mailing list thread), the object is retained when the block is queued up and released when the block finishes.
First of all, dispatch_queue_t is not a Core Foundation object.
Dispatch objects are considered Objective-C objects by the compiler (for ARC and blocks purposes) if your deployment target is iOS 6+ / OS X 10.8+.

Get underlying dispatch_queue_t from NSOperationQueue

I seem to have some confusion between dispatch_queue_t and NSOperationQueue queues.
By default, AFNetworking's AFImageRequestOperation will execute the success callback block on the application's main thread. To change this, AFHTTPRequestOperation has the property successCallbackQueue which lets you choose on which queue to run the callback.
I'm trying to execute the success callback on the same background queue / background threads which already did the HTTP request. Instead of returning to the main thread, the NSOperationQueue which ran the HTTP request should run the callback as well, since there are some heavy calculations I need to do using some of the returned images.
My first try was to set successCallbackQueue to the NSOperationQueue instance on which the AFImageRequestOperation ran. However, the successCallbackQueue property is of type dispatch_queue_t, so I need a way to get the underlying dispatch_queue_t of my NSOperation instance, if there is such a thing.
Is that possible, or do I need to create a separate dispatch_queue_t?
The reason I ask: It's somewhat strange that AFNetworking inherits from NSOperation, but expects us to use dispatch_queue_t queues for the callbacks. Kind of mixing the two paradigmas dispatch_queue_t and NSOperationQueue.
Thanks for any hints!
There is no such thing, there isn't a one-to-one correspondence of an NSOperationQueue and a dispatch_queue_t, the queueing concepts in the two APIs are very different (e.g. NSOperationQueue does not have strict FIFO queueing like GCD does).
The only dispatch queue used by NSOperationQueue to execute your code is the default priority global concurrent queue.
NSOperationQueue is not your bottleneck with AFNetworking. Request operations are bound by network, not CPU or memory. All of the work is done asynchronously in dispatch queues, which are accessible as properties in AFHTTPRequestOperation.
It is not advisable to use the network thread to do any processing. This will not improve performance in any way.
Instead, if you're noticing performance issues, try limiting the maximum number of concurrent operations in the operation queue, as a way to indirectly control the amount of work being done by those background processing queues.
It is interesting that AFHTTPClient uses an NSOperationQueue to run the AFHTTPRequestOperations but GCD dispatch_queues to handle the results.
Regarding NSOperationQueue the Apple docs say:
Note: In iOS 4 and later, operation queues use Grand Central Dispatch to execute operations.
but there doesn't seem to be a public API to get the dispatch_queue for a given operation.
If it's not that important to you that the success callback has to be on exactly the same queue/thread that the original operation was executed why not set:
successCallbackQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0);
XCode 6.4 for iOS 8.4, ARC enabled
1) "...so I need a way to get the underlying dispatch_queue_t of my NSOperation instance, if there is such a thing."
There is a property of NSOperationQueue that can help:
#property(assign) dispatch_queue_t underlyingQueue
It can be used as follows to assign to NSOperationQueue:
NSOperationQueue *concurrentQueueForServerCommunication = [[NSOperationQueue alloc] init];
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
concurrentQueueForServerCommunication.underlyingQueue = concurrentQueue;
Or assign from NSOperationQueue:
NSOperationQueue *concurrentQueueForServerCommunication = [[NSOperationQueue alloc] init];
dispatch_queue_t concurrentQueue = concurrentQueueForServerCommunication.underlyingQueue;
Not sure if the API you are using for network communication updates your UI after the network task's completion, but just in case it does not, then you must know to get back onto the main queue when the completion block is executed:
dispatch_sync(dispatch_get_main_queue(), ^{
//Update your UI here...
}
Hope this helps! Cheers.
First of all, it's a good behaviour to execute the success of the AFImageRequestOperation on the main thread because the main usage of this operation is to download an image in background and display it on the UI (which should be on the main thread), but in order to satisfy the needs of those user (your case too) who want to execute the callback on other threads, there is also a successCalbackQueue.
So you can create your own dispatch_queue_t with dispatch_queue_create method or the recommended way, you should use dispatch_get_global_queue to get the main queue.
On each case, make sure that in your success block, if you are making some changes to the UI, put them inside dispatch_async(dispatch_get_main_queue(), ^{ // main op here});
Swift 3 code, based on #serge-k's answer:
// Initialize the operation queue.
let operationQueue = OperationQueue()
operationQueue.name = "com.example.myOperationQueue"
operationQueue.qualityOfService = .userInitiated
// Initialize a backing DispatchQueue so we can reuse it for network operations.
// Because no additional info is give, the dispatch queue will have the same QoS as the operation queue.
let operationQueueUnderlyingQueue = DispatchQueue(label: "com.example.underlyingQueue")
operationQueue.qualityOfService.underlyingQueue = operationQueueUnderlyingQueue
You can then use this in Alamofire (or AFNetworking) in the following manner:
Alamofire.request("https://example.com/get", parameters: nil).validate().responseJSON(queue: operationQueue.underlyingQueue) { response in
response handler code
}
A warning here, from Apple's documentation on setting the OperationQueue's underlying queue:
The value of this property should only be set if there are no operations in the queue; setting the value of this property when operationCount is not equal to 0 raises an invalidArgumentException. The value of this property must not be the value returned by dispatch_get_main_queue(). The quality-of-service level set for the underlying dispatch queue overrides any value set for the operation queue's qualityOfService property.

`[NSThread isMainThread]` always returns YES

This code
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(#"Main Thread? %d", [NSThread isMainThread]);
});
shows that I'm in the main thread. Even doing this:
queue = dispatch_queue_create("nonMainQueue", NULL);
still reports that I'm in the main queue. This is, it seems, because I'm using dispatch sync.
Does this mean that my code is the same as not using dispatch_sync at all? Also: what's the point of dispatch_sync if it does nothing at all, then?
Because queues are not threads, in order to check if you are on the main 'queue', you must use different code, something similar to this:
if (dispatch_get_current_queue() == dispatch_get_main_queue()) {
NSLog(#"On Main Thread!");
}
Just note that dispatch_get_current_queue is deprecated, and is subject to be completely removed in a later iOS/Mac OS version.
This is documented behavior. As an optimization the blocks passed to dispatch_sync are executed on the current thread if possible (which is almost always).
My understanding from Apple's GCD guide, there is no guarantee that dispatch queues will execute on a separate thread. GCD will determine which thread, and if necessary create a new one.
Part of the point is now you do not have to think about threads.
The only thing to keep in mind, is to make sure you are updating UI elements on the main queue, for example:
// on background queue
dispatch_async( dispatch_get_main_queue(), ^(void){
someLabel.text = #"My Text";
});

Threading NSStream

I have a TCP connection that is open continuously for communication with an external device. There is a lot going on in the communication pipe which causes the UI to become unresponsive at times.
I would like to put the communication on a separate thread. I understand detachNewThread and how it calls a #selector. My issue is that I am not sure how this would be used in conjunction with something like NSStream?
Rather than manually creating a thread and managing thread safety issues, you might prefer to use Grand Central Dispatch ('GCD'). That allows you to post blocks — which are packets of code and some state — off to be executed away from the main thread and wherever the OS thinks is most appropriate. If you create a serial dispatch queue you can even be certain that if you post a new block while an old one has yet to finish, the system will wait until it finishes.
E.g.
// you'd want to make this an instance variable in a real program
dispatch_queue_t serialDispatchQueue =
dispatch_queue_create(
"A handy label for debugging",
DISPATCH_QUEUE_SERIAL);
...
dispatch_async(serialDispatchQueue,
^{
NSLog(#"all code in here occurs on the dispatch queue ...");
});
/* lots of other things here */
dispatch_async(serialDispatchQueue,
^{
NSLog(#"... and this won't happen until after everything already dispatched");
});
...
// cleanup, for when you're done
dispatch_release(serialDispatchQueue);
A very quick introduction to GCD is here, Apple's more thorough introduction is also worth reading.

Resources