Default value of maxConcurrentOperationCount for NSOperationQueue

maxConcurrentOperationCount of NSOperationQueue can only set from 1 to 6 on iPhone Platform .

It is not a good idea to set maxConcurrentOperationCount of NSOperationQueue as default value. As it means an unlimited number of queues.

When should the maxConcurrentOperationCount of NSOperationQueue be set from 1 to 6 ?

When multiple tasks can be invoked simultaneously, the maxConcurrentOperationCount of NSOperationQueue should be set as 6. For example, multiple images can be downloaded at the same time.

Why is it six specifically instead of 10 or 100?

In the framework named SDWebImage , we can see the source code as follows:

- (instancetype)init {
   self = [super init];
   if (self) {
       _maxConcurrentDownloads = 6;
       _downloadTimeout = 15.0;
       _executionOrder = SDWebImageDownloaderFIFOExecutionOrder;
       _acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
   }
   return self;
}

This code means that the default value is 6. SDWebImage Reference SDWebImageDownloaderConfig Class Reference

the meaning of maxConcurrentDownloads is same as maxConcurrentOperationCount (Apple-Docs-NSOperationQueue-maxConcurrentOperationCount )

Why is it six specifically instead of 10 or 100?

The coder of SDWebImage said it is via experimentation:

Reference: Why SDWebImageDownloader maxConcurrentOperationCount = 6? #1961

Neglect the experimentation, and the maxConcurrentOperationCount of NSOperationQueue should be set from 1 to 6 because the maximum number of processor cores on the iOS platform is six from iPhone to iPhone 13 /13 Pro/13 Pro Max.

iPhone First released system on number of processor cores
iPhone 2007 S (Samsung S5L8900) 1 core
iPhone 3G 2008 S (Samsung S5L8900) 1 core
iPhone 3GS 2009 S (Samsung S5PC100 1 core
iPhone 4 2010 A4 1 core
iPhone 4S 2011 A5 2 cores
iPhone 5 2012 A6 2 cores
iPhone 5S 2013 A7 2cores
iPhone 6/6 plus 2014 A8 2 cores
iPhone 6s/6s plus/SE 2015 A9 2 cores
iPhone 7/plus 2016 A9 4 cores (2 cores used)
iPhone 8/plus/X 2017 A11 6 cores = 2 high power Monsoon cores + 4 low power Mistral cores
iPhone XS/XS MAX/XR 2018 A12 6 cores = 2 high power Vortex cores + 4 low power Tempest cores
iPhone 11 /11 Pro/11 Pro Max 2019 A13 6 cores = 2 performance + 4 efficiency cores
iPhone 12 /12 Pro/12 Pro Max 2020 A14 6 cores = 2 performance + 4 efficiency cores
iPhone 13 /13 Pro/13 Pro Max 2021 A15 6 cores = 2 performance + 4 efficiency cores

We can print the number of processor cores with the code as follows:

   NSUInteger cpuCoresCount = [[NSProcessInfo processInfo] processorCount];

We can print the number of active processor cores with the code as follows:

   NSUInteger activeCPUCoresCount = [[NSProcessInfo processInfo] activeProcessorCount];

Reference Docs: Apple-Docs-NSProcessInfo-processorCount

It is reasonable to set _maxConcurrentDownloads on the iPhone Platform as the count of CPU cores. As for the further comment, activeProcessorCount may be better.

In the framework named YYKit, queueCount is set as activeProcessorCount.

#define MAX_QUEUE_COUNT 16
   static int queueCount;
   static dispatch_queue_t queues[MAX_QUEUE_COUNT];
   static dispatch_once_t onceToken;
   static int32_t counter = 0;
   dispatch_once(&onceToken, ^{
       queueCount = (int)[NSProcessInfo processInfo].activeProcessorCount;
       queueCount = queueCount < 1 ? 1 : queueCount > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : queueCount;
       if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
           for (NSUInteger i = 0; i < queueCount; i++) {
               dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0);
               queues[i] = dispatch_queue_create("com.ibireme.yykit.render", attr);
           }
       } else {
           for (NSUInteger i = 0; i < queueCount; i++) {
               queues[i] = dispatch_queue_create("com.ibireme.yykit.render", DISPATCH_QUEUE_SERIAL);
               dispatch_set_target_queue(queues[i], dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
           }
       }
   });
   int32_t cur = OSAtomicIncrement32(&counter);
   if (cur < 0) cur = -cur;
   return queues[(cur) % queueCount];
#undef MAX_QUEUE_COUNT

When should the maxConcurrentOperationCount of NSOperationQueue be set as 1 ?

They are setting maxConcurrentOperationCount as one means that only one task can be invoked simultaneously.

When only one task can be invoked simultaneously, maxConcurrentOperationCount should be set as 1. For example, only one GIF image can be animated simultaneously; only one video can be played at the same time. In addition, only one video/image can be decoded simultaneously.

Reference: Apple-Docs-NSOperationQueue-maxConcurrentOperationCount


From the documentation,

The maximum number of concurrent operations set explicitly on the receiver using the setMaxConcurrentOperationCount: method. If no value has been explicitly set, this method returns NSOperationQueueDefaultMaxConcurrentOperationCount by default.

So it is NSOperationQueueDefaultMaxConcurrentOperationCount. If this is set, it will choose an appropriate value based on the number of available processors and other relevant factors.

This is how it is defined:

enum {
  NSOperationQueueDefaultMaxConcurrentOperationCount = -1
};

NSOperationQueueDefaultMaxConcurrentOperationCount: The default maximum number of operations is determined dynamically by the NSOperationQueue object based on current system conditions.


In one of my apps, I add about 35k instances of NSOperation to an NSOperationQueue at once. If I set maxConcurrentOperationCount to 64, I gain about 20x of performance compared to the default value. The CPU load rises from ~120% to 400% which seems to indicate that the default value is so high that the CPU is mostly busy working on the op queue overhead.

Conclusion: if you have many short lived NSOperation it might be worth to play with the maxConcurrentOperationCount.