NSOperationQueue, concurrent operation and thread

I am developing a kind of application for quick image scanning.
In the -captureOutput:didOutputSampleBuffer:fromConnection: I take CVPixelBuffer and add it to the NSOperation subclass. NSOperation takes a buffer and converts it to an image stored in the file system.
The -isConcurrent NSOperation method returns YES. After the operation is created, it is added to the NSOperationQueue .
Everything is working fine, except for one problem that affects the scan rate. Using the time profiler, I found that some of the NSOperation runs on the same thread of the AVCaputureVideoOutput delegate that I created:

 dispatch_queue_t queue = dispatch_queue_create("it.CloudInTouchLabs.avsession", DISPATCH_QUEUE_SERIAL); [dataOutput setSampleBufferDelegate:(id)self queue:queue]; 

When an operation is performed in the same thread in the AV session queue, it affects the frame rate, this only happens with some of them, probably, the GCD makes a decision under the hood manager on active threads.
The only method I "We found a solution to this problem is to create a separate thread and pass it to the individual operations, forcing them to run on it.
Is there any other way to get the operations to work in another thread?

[EDIT]
Following @impcc's recommendations, I did some tests.
The results are quite interesting, even if they are incompatible. The test platform is the iPhone 5, connected in debug mode via MBP with a 16-gigabyte quad-core i7 processor. The session has a 60fps output, tested using the algorithm in the Apple RosyWriter sample code.
High priority queue-oriented AVSession queue synchronization

  • 26 fps, where sometimes the queue stream is shared with one operation. Time spent inside delegate methods has an average value of 0.02 s

  • 14 fps with a thread created only for operations, if the main method is not called on this thread, the launch will be forced to execute on this particular thread. This thread is created once and is kept alive with a dummy port. The time spent inside the delegate is 0.008.

AVSession Queue Parallel to High Priority Target Queue

  • 13.5 fps, where sometimes the queue stream is shared with one of the operations. The time spent on delegate methods is, on average, 0.02 s, equivalent to an analog agreement with a synchronized queue.

  • 14 fps with a thread created only for operations, if the main method is not called on this thread, the launch will be forced to execute on this particular thread. This thread is created once and kept alive with a dummy port. The time spent inside the delegate is 0.008.

Conclusion
A parallel or sequential queue does not seem to matter much, however, for me, parallel operation is not a good reason for the timestamps I need to take in order to maintain a single image sequence. The fact that surprises me is the frame drop using the ad-hoc stream, even if the time taken inside the delegate method is much less, the frame rate drops by about 10 frames per second. Just trusting the GCD, the frame rate is better, but the delegate methods take more than 2 times, which is probably due to the fact that sometimes the AV queue is also used by nsoperations.
I cannot understand why the time spent inside the delegate does not seem to correlate with fps. Shouldn't be faster the better?
Further research really seems that the time si was stolen is in the process of adding and, probably, performing operations in the queue.

+1
source share
1 answer

I think you may not understand the meaning of isConcurrent . This is understandable, as it is incredibly poorly named. When you return YES from -isConcurrent , what does this really mean, I "handle any concurrency needs associated with this operation, and otherwise it will behave asynchronously." In this situation, NSOperationQueue can freely call -start on your operation synchronously in the thread from which you add the operation. NSOperationQueue expects that since you stated that you will be managing your own concurrency, your -start method -start simply start the asynchronous process and return immediately. I suspect this is the source of your problem.

If you performed your operation by overriding -main , you would almost certainly want to return NO from isConcurrent . To complicate matters, the behavior associated with isConcurrent has changed over the years (but all that is described in isConcurrent papers). A fantastic explanation of how to implement return correctly - YES -from- isConcurrent NSOperation can be found here.

As I read your question here, it doesn't look like your NSOperation subclass really needs to "manage its own concurrency", but rather just wants it to execute asynchronously, "to the background thread", potentially "simultaneously" with other operations.

In terms of providing the best performance for your AVCaptureVideoDataOutputSampleBufferDelegate I would suggest making the queue you go into -setSampleBufferDelegate:queue: be (itself) at the same time and that it targets a high priority global parallel queue, for example:

 dispatch_queue_t queue = dispatch_queue_create("it.CloudInTouchLabs.avsession", DISPATCH_QUEUE_CONCURRENT); dispatch_set_target_queue(queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)); [dataOutput setSampleBufferDelegate:(id)self queue:queue]; 

Then you should make the delegate callback methods as easy as possible - just pack the information needed to create the NSOperation and add it to the NSOperationQueue .

This should ensure that callbacks always take precedence over NSOperations . (I understand that NSOperationQueue is for the main queue (for the NSOperationQueue associated with the main thread and the run loop) or the default for the background priority queue.) This should allow your callbacks to fully support the frame rate.

Another important thing that needs to be removed here (as another commentator noted) is that if you use all your concurrency using GCD, then there is only one special thread - the main thread. In addition, threads are just a common resource that a GCD can (and will) use interchangeably with each other. The fact that the thread with thread ID X was used at one point to serve the delegate callback, and at another point was used for your processing, does not in itself indicate a problem.

Hope this helps!

+7
source

Source: https://habr.com/ru/post/983721/


All Articles