When designing concurrent code in Swift, you might wonder which API to pick among the diversity of available choices. In this article we will benchmark performance of most notable Apple locking APIs and suggest best options based on their characteristics.
Locking APIs and Atomicity
We’ve already covered major locking APIs as well as concurrent programming concepts in Atomic Properties in Swift, so make sure you’ve checked this article before moving forward.
First off, let’s describe sampling data and the way it has been collected.
|API under test||Name on chart|
To benchmark each API a class has been implemented with a critical section in its setter and getter.
Main benchmark function:
To compensate deviations of individual benchmark iterations, each data sample is calculated 100 times and an average value is taken.
Whole app source code can be found here: https://github.com/V8tr/AtomicBenchmark. It implements an abstraction over the above two methods, runs test samples and exports statistics to a CSV file.
Locks have roughly equal performance, with
NSLock being a bit slower than the others.
OperationQueue is way slower than
DispatchQueue is 7-8 times slower than locks,
OperationQueue is ~20 times slower than the dispatch queue and 140-160 times slower than locks.
Same as with getters, all locks have roughly equal performance and
NSLock is a bit slower than the rest. Variance of statistic is higher, comparing to the same calculations for getters.
Comparing to getters,
OperationQueue falls behind
DispatchQueue even more.
DispatchQueue is 3-4 times slower than locks,
OperationQueue is ~70 times slower than the dispatch queue and 220-250 times slower than locks.
Comparing Setters vs. Getters
OperationQueue have considerable variance of setter vs. getter performance, locks are approximately equal.
Based on the benchmark results,
DispatchQueue must be your best choice for creating a critical section in your code.
Under 10000 calculations it performs almost identical to locks, while providing higher-level and thus less error-prone API.
If for some reason the block-based locking nature of
DispatchQueue is not what you need, I’d suggest to go with
NSLock. It’s a bit more heavyweight than the rest of the locks, but this can be neglected.
Pthread locks are usually a bad choice due to considerably complex configuration and some usage nuances, as highlighted in Atomic Properties in Swift.
Thanks for reading!
If you enjoyed this post, be sure to follow me on Twitter to not miss any new content.