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
.