Skip to content

Instantly share code, notes, and snippets.

@steipete
Forked from RomanTruba/Synchronization_test_iOS_SDK10
Last active August 29, 2023 08:47
Show Gist options
  • Save steipete/36350a8a60693d440954b95ea6cbbafc to your computer and use it in GitHub Desktop.
Save steipete/36350a8a60693d440954b95ea6cbbafc to your computer and use it in GitHub Desktop.
Updated for Xcode 8, Swift 3; added os_unfair_lock
//
// SpinlockTestTests.swift
// SpinlockTestTests
//
// Created by Peter Steinberger on 04/10/2016.
// Copyright © 2016 PSPDFKit GmbH. All rights reserved.
//
import XCTest
final class LockingTests: XCTestCase {
func testSpinLock() {
var spinLock = OS_SPINLOCK_INIT
executeLockTest { (block) in
OSSpinLockLock(&spinLock)
block()
OSSpinLockUnlock(&spinLock)
}
}
func testUnfairLock() {
var unfairLock = os_unfair_lock_s()
executeLockTest { (block) in
os_unfair_lock_lock(&unfairLock)
block()
os_unfair_lock_unlock(&unfairLock)
}
}
func testDispatchSemaphore() {
let sem = DispatchSemaphore(value: 1)
executeLockTest { (block) in
_ = sem.wait(timeout: DispatchTime.distantFuture)
block()
sem.signal()
}
}
func testNSLock() {
let lock = NSLock()
executeLockTest { (block) in
lock.lock()
block()
lock.unlock()
}
}
func testPthreadMutex() {
var mutex = pthread_mutex_t()
pthread_mutex_init(&mutex, nil)
executeLockTest{ (block) in
pthread_mutex_lock(&mutex)
block()
pthread_mutex_unlock(&mutex)
}
pthread_mutex_destroy(&mutex);
}
func testSyncronized() {
let obj = NSObject()
executeLockTest{ (block) in
objc_sync_enter(obj)
block()
objc_sync_exit(obj)
}
}
func testQueue() {
let lockQueue = DispatchQueue.init(label: "com.test.LockQueue")
executeLockTest{ (block) in
lockQueue.sync() {
block()
}
}
}
func disabled_testNoLock() {
executeLockTest { (block) in
block()
}
}
private func executeLockTest(performBlock:@escaping (_ block:() -> Void) -> Void) {
let dispatchBlockCount = 16
let iterationCountPerBlock = 100_000
// This is an example of a performance test case.
let queues = [
DispatchQueue.global(qos: DispatchQoS.QoSClass.userInteractive),
DispatchQueue.global(qos: DispatchQoS.QoSClass.default),
DispatchQueue.global(qos: DispatchQoS.QoSClass.utility),
]
var value = 0
self.measure {
let group = DispatchGroup.init()
for block in 0..<dispatchBlockCount {
group.enter()
let queue = queues[block % queues.count]
queue.async(execute: {
for _ in 0..<iterationCountPerBlock {
performBlock({
value = value + 2
value = value - 1
})
}
group.leave()
})
}
_ = group.wait(timeout: DispatchTime.distantFuture)
}
}
}
@steipete
Copy link
Author

steipete commented Oct 4, 2016

Run on my latest gen rMBP, max specs.

os_unfair_lock is new in iOS 10 and Sierra and is the fastest option.
OSSpinLock and dispatch semaphores don't donate priorities and should not be used.
NSLock is basically pthread_mutex + objc_msgSend
queues can be slow
@synchronized needs to lock twice because it works with an arbitrary object that is not itself a lock object. (thanks, Greg!)

  • testUnfairLock (4.014 seconds)
  • testSpinLock (4.064 seconds)
  • testDispatchSemaphore (46.611 seconds)
  • testPthreadMutex (64.438 seconds)
  • testNSLock (68.508 seconds)
  • testQueue (67.629 seconds)
  • testSynchronized (70.172 seconds)

At PSPDFKit we now use a wrapper for os_unfair_lock that falls back to pthread_mutex on iOS 9.

TODO: Rewrite in ObjC++ to make sure Swift doesn't change any of the results here.
TODO: Measure std::mutex, std::recursive_mutex and std::lock_guard

Full output:

Test Suite 'LockingTests' started at 2016-10-04 02:08:07.224
Test Case '-[SpinlockTestTests.LockingTests testDispatchSemaphore]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:112: Test Case '-[SpinlockTestTests.LockingTests testDispatchSemaphore]' measured [Time, seconds] average: 4.597, relative standard deviation: 6.380%, values: [4.886180, 4.999205, 4.881644, 4.604738, 4.695120, 4.536256, 4.741501, 4.102249, 4.389588, 4.136712], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testDispatchSemaphore]' passed (46.611 seconds).
Test Case '-[SpinlockTestTests.LockingTests testNSLock]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:112: Test Case '-[SpinlockTestTests.LockingTests testNSLock]' measured [Time, seconds] average: 6.826, relative standard deviation: 5.460%, values: [6.473827, 7.236301, 6.677733, 6.874861, 7.061250, 7.469417, 6.973959, 6.729044, 6.084627, 6.677015], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testNSLock]' passed (68.508 seconds).
Test Case '-[SpinlockTestTests.LockingTests testPthreadMutex]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:112: Test Case '-[SpinlockTestTests.LockingTests testPthreadMutex]' measured [Time, seconds] average: 6.412, relative standard deviation: 4.873%, values: [7.102613, 6.491664, 6.423482, 6.468268, 6.446051, 6.353700, 6.567530, 5.826615, 6.070497, 6.371068], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testPthreadMutex]' passed (64.438 seconds).
Test Case '-[SpinlockTestTests.LockingTests testQueue]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:112: Test Case '-[SpinlockTestTests.LockingTests testQueue]' measured [Time, seconds] average: 6.732, relative standard deviation: 8.336%, values: [6.567437, 6.290663, 6.224900, 7.248803, 7.663676, 7.682304, 6.609493, 6.621567, 6.359084, 6.049725], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testQueue]' passed (67.629 seconds).
Test Case '-[SpinlockTestTests.LockingTests testSpinLock]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:112: Test Case '-[SpinlockTestTests.LockingTests testSpinLock]' measured [Time, seconds] average: 0.374, relative standard deviation: 5.649%, values: [0.389807, 0.389681, 0.337419, 0.361176, 0.386480, 0.388130, 0.331918, 0.386507, 0.382785, 0.384084], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testSpinLock]' passed (4.064 seconds).
Test Case '-[SpinlockTestTests.LockingTests testSyncronized]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:112: Test Case '-[SpinlockTestTests.LockingTests testSyncronized]' measured [Time, seconds] average: 6.986, relative standard deviation: 3.157%, values: [7.058685, 6.999750, 7.234801, 7.101809, 6.630196, 6.507727, 7.036769, 7.023626, 7.090010, 7.174353], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testSyncronized]' passed (70.172 seconds).
Test Case '-[SpinlockTestTests.LockingTests testUnfairLock]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:112: Test Case '-[SpinlockTestTests.LockingTests testUnfairLock]' measured [Time, seconds] average: 0.369, relative standard deviation: 1.875%, values: [0.380288, 0.374790, 0.368111, 0.366461, 0.362047, 0.374221, 0.367991, 0.377703, 0.361819, 0.358630], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testUnfairLock]' passed (4.014 seconds).
Test Suite 'LockingTests' passed at 2016-10-04 02:13:32.664.
Executed 7 tests, with 0 failures (0 unexpected) in 325.436 (325.440) seconds
Test Suite 'SpinlockTestTests.xctest' passed at 2016-10-04 02:13:32.664.
Executed 7 tests, with 0 failures (0 unexpected) in 325.436 (325.441) seconds
Test Suite 'All tests' passed at 2016-10-04 02:13:32.665.
Executed 7 tests, with 0 failures (0 unexpected) in 325.436 (325.442) seconds

Here's the output with optimizations on. Queues fair better:

2016-10-04 02:32:14.588541 SpinlockTest[32146:1734978] subsystem: com.apple.UIKit, category: HIDEventFiltered, enable_level: 0, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 1, privacy_setting: 2, enable_private_data: 0
2016-10-04 02:32:14.601262 SpinlockTest[32146:1734978] subsystem: com.apple.UIKit, category: HIDEventIncoming, enable_level: 0, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 1, privacy_setting: 2, enable_private_data: 0
2016-10-04 02:32:14.612360 SpinlockTest[32146:1734977] subsystem: com.apple.BaseBoard, category: MachPort, enable_level: 1, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 0, privacy_setting: 0, enable_private_data: 0
2016-10-04 02:32:14.626560 SpinlockTest[32146:1734928] subsystem: com.apple.UIKit, category: StatusBar, enable_level: 0, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 1, privacy_setting: 2, enable_private_data: 0
2016-10-04 02:32:14.658214 SpinlockTest[32146:1734928] subsystem: com.apple.BackBoardServices.fence, category: App, enable_level: 1, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 0, privacy_setting: 0, enable_private_data: 0
Test Suite 'All tests' started at 2016-10-04 02:32:16.566
Test Suite 'SpinlockTestTests.xctest' started at 2016-10-04 02:32:16.567
Test Suite 'LockingTests' started at 2016-10-04 02:32:16.567
Test Case '-[SpinlockTestTests.LockingTests testDispatchSemaphore]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:112: Test Case '-[SpinlockTestTests.LockingTests testDispatchSemaphore]' measured [Time, seconds] average: 4.534, relative standard deviation: 9.189%, values: [4.750716, 4.846857, 4.696196, 4.691405, 4.641887, 4.558686, 4.670412, 3.795300, 3.677315, 5.013317], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testDispatchSemaphore]' passed (45.785 seconds).
Test Case '-[SpinlockTestTests.LockingTests testNSLock]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:112: Test Case '-[SpinlockTestTests.LockingTests testNSLock]' measured [Time, seconds] average: 6.705, relative standard deviation: 6.017%, values: [6.760171, 6.893496, 7.339591, 6.802631, 6.685971, 6.783347, 6.629533, 6.759303, 5.628764, 6.764179], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testNSLock]' passed (67.369 seconds).
Test Case '-[SpinlockTestTests.LockingTests testPthreadMutex]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:112: Test Case '-[SpinlockTestTests.LockingTests testPthreadMutex]' measured [Time, seconds] average: 6.535, relative standard deviation: 4.741%, values: [6.593796, 6.599546, 6.582593, 6.648490, 6.587857, 6.531706, 5.835824, 6.236867, 7.127968, 6.606340], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testPthreadMutex]' passed (65.600 seconds).
Test Case '-[SpinlockTestTests.LockingTests testQueue]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:112: Test Case '-[SpinlockTestTests.LockingTests testQueue]' measured [Time, seconds] average: 6.032, relative standard deviation: 6.229%, values: [6.115642, 6.120994, 5.920505, 6.051356, 6.130284, 6.073577, 5.228097, 5.769650, 6.067827, 6.842238], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testQueue]' passed (60.575 seconds).
Test Case '-[SpinlockTestTests.LockingTests testSpinLock]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:112: Test Case '-[SpinlockTestTests.LockingTests testSpinLock]' measured [Time, seconds] average: 0.380, relative standard deviation: 5.884%, values: [0.400947, 0.396058, 0.341312, 0.397222, 0.394180, 0.332765, 0.381286, 0.387111, 0.383781, 0.381317], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testSpinLock]' passed (4.047 seconds).
Test Case '-[SpinlockTestTests.LockingTests testSyncronized]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:112: Test Case '-[SpinlockTestTests.LockingTests testSyncronized]' measured [Time, seconds] average: 7.079, relative standard deviation: 7.108%, values: [7.764845, 7.636262, 7.127368, 6.920391, 5.804514, 7.036886, 7.080184, 7.068745, 6.979611, 7.374604], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testSyncronized]' passed (71.111 seconds).
Test Case '-[SpinlockTestTests.LockingTests testUnfairLock]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:112: Test Case '-[SpinlockTestTests.LockingTests testUnfairLock]' measured [Time, seconds] average: 0.389, relative standard deviation: 6.832%, values: [0.390612, 0.429027, 0.322254, 0.412703, 0.386765, 0.379714, 0.380070, 0.387980, 0.398117, 0.402645], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testUnfairLock]' passed (4.142 seconds).
Test Suite 'LockingTests' passed at 2016-10-04 02:37:35.199.
Executed 7 tests, with 0 failures (0 unexpected) in 318.629 (318.632) seconds
Test Suite 'SpinlockTestTests.xctest' passed at 2016-10-04 02:37:35.200.
Executed 7 tests, with 0 failures (0 unexpected) in 318.629 (318.633) seconds
Test Suite 'All tests' passed at 2016-10-04 02:37:35.200.
Executed 7 tests, with 0 failures (0 unexpected) in 318.629 (318.635) seconds

Test session log:
/Users/steipete/Builds/SpinlockTest-fscqadvarowcokhlhoxqbxpavofv/Logs/Test/0319717B-9EE1-4603-88E6-D352B8EB8CA5/Session-SpinlockTestTests-2016-10-04_023201-YheNo0.log

A naive lock test that just tests performance, not queue interaction/priority donation/fairness

self.measure {
            for _ in 0..<iterationCountPerBlock {
                performBlock({
                    value = value + 2
                    value = value - 1
                })
            }

Test Case '-[SpinlockTestTests.LockingTests testPthreadMutex]' passed (0.456 seconds).
Test Case '-[SpinlockTestTests.LockingTests testSpinLock]' passed (0.463 seconds).
Test Case '-[SpinlockTestTests.LockingTests testUnfairLock]' passed (0.476 seconds).
Test Case '-[SpinlockTestTests.LockingTests testNSLock]' passed (0.548 seconds).
Test Case '-[SpinlockTestTests.LockingTests testQueue]' passed (0.727 seconds).
Test Case '-[SpinlockTestTests.LockingTests testSyncronized]' passed (0.753 seconds).

Test Suite 'All tests' started at 2016-10-04 02:39:36.740
Test Suite 'SpinlockTestTests.xctest' started at 2016-10-04 02:39:36.741
Test Suite 'LockingTests' started at 2016-10-04 02:39:36.742
Test Case '-[SpinlockTestTests.LockingTests testDispatchSemaphore]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:122: Test Case '-[SpinlockTestTests.LockingTests testDispatchSemaphore]' measured [Time, seconds] average: 0.019, relative standard deviation: 13.977%, values: [0.019160, 0.017707, 0.018289, 0.017111, 0.017115, 0.017454, 0.017531, 0.017128, 0.020741, 0.026009], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testDispatchSemaphore]' passed (0.530 seconds).
Test Case '-[SpinlockTestTests.LockingTests testNSLock]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:122: Test Case '-[SpinlockTestTests.LockingTests testNSLock]' measured [Time, seconds] average: 0.023, relative standard deviation: 10.700%, values: [0.030021, 0.022150, 0.022142, 0.022582, 0.022122, 0.021585, 0.022290, 0.025837, 0.022220, 0.022302], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testNSLock]' passed (0.548 seconds).
Test Case '-[SpinlockTestTests.LockingTests testPthreadMutex]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:122: Test Case '-[SpinlockTestTests.LockingTests testPthreadMutex]' measured [Time, seconds] average: 0.014, relative standard deviation: 9.041%, values: [0.013749, 0.013880, 0.014246, 0.015305, 0.015638, 0.011954, 0.012159, 0.013013, 0.012167, 0.013104], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testPthreadMutex]' passed (0.456 seconds).
Test Case '-[SpinlockTestTests.LockingTests testQueue]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:122: Test Case '-[SpinlockTestTests.LockingTests testQueue]' measured [Time, seconds] average: 0.044, relative standard deviation: 6.836%, values: [0.046690, 0.041579, 0.045586, 0.043581, 0.048864, 0.045214, 0.044227, 0.040722, 0.039256, 0.048439], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testQueue]' passed (0.727 seconds).
Test Case '-[SpinlockTestTests.LockingTests testSpinLock]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:122: Test Case '-[SpinlockTestTests.LockingTests testSpinLock]' measured [Time, seconds] average: 0.015, relative standard deviation: 10.816%, values: [0.015732, 0.014220, 0.014467, 0.014261, 0.014189, 0.014985, 0.014291, 0.017602, 0.010890, 0.014674], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testSpinLock]' passed (0.463 seconds).
Test Case '-[SpinlockTestTests.LockingTests testSyncronized]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:122: Test Case '-[SpinlockTestTests.LockingTests testSyncronized]' measured [Time, seconds] average: 0.043, relative standard deviation: 17.814%, values: [0.039555, 0.039760, 0.065413, 0.043142, 0.039570, 0.039196, 0.039811, 0.042163, 0.039297, 0.040386], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testSyncronized]' passed (0.753 seconds).
Test Case '-[SpinlockTestTests.LockingTests testUnfairLock]' started.
/Users/steipete/Documents/Projects/Stuff/SpinlockTest/SpinlockTestTests/SpinlockTestTests.swift:122: Test Case '-[SpinlockTestTests.LockingTests testUnfairLock]' measured [Time, seconds] average: 0.015, relative standard deviation: 7.026%, values: [0.017713, 0.014448, 0.015184, 0.015010, 0.015095, 0.015195, 0.016372, 0.015228, 0.014644, 0.013506], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[SpinlockTestTests.LockingTests testUnfairLock]' passed (0.476 seconds).
Test Suite 'LockingTests' passed at 2016-10-04 02:39:40.700.
Executed 7 tests, with 0 failures (0 unexpected) in 3.953 (3.958) seconds
Test Suite 'SpinlockTestTests.xctest' passed at 2016-10-04 02:39:40.700.
Executed 7 tests, with 0 failures (0 unexpected) in 3.953 (3.959) seconds
Test Suite 'All tests' passed at 2016-10-04 02:39:40.701.
Executed 7 tests, with 0 failures (0 unexpected) in 3.953 (3.960) seconds

Test session log:
/Users/steipete/Builds/SpinlockTest-fscqadvarowcokhlhoxqbxpavofv/Logs/Test/AA68633C-FEA2-4400-98C1-E1762EE93EFB/Session-SpinlockTestTests-2016-10-04_023926-1kYo0E.log

@Pearapps
Copy link

I am pretty ignorant about the tradeoffs here between the different kinds of locks and systems here - is there a good resource that describes this with more detail?

@steipete
Copy link
Author

For most CRUD-apps it will not matter. We're building a PDF renderer and SDK, and when you have hot code paths that are called 10.000 times during a render operation, these small things start to matter. Then again, using the old spin locks can lifelock your app, but these are already deprecated and you'll get a warning there anyway. Dispatch queues are very nice for most simple things.

@RomanTruba
Copy link

Also updated source test file

@iby
Copy link

iby commented May 17, 2018

@Vadim-Yelagin
Copy link

Could you please re-measure the tests on latest software?
I'm seeing a huge drop in performance (around x8 times) for queues and semaphores on Xcode 10, iOS 12.

@drewster99
Copy link

Wow, DispatchSemaphore is terrible now. I suppose someone ought to file a radar...

@couchdeveloper
Copy link

couchdeveloper commented Jun 10, 2019

@drewster99 I ran a similar test and it seems, NSLock, pthread_mutex and os_unfair_lock all are pretty close and much faster than these test results above suggest. This difference is probably the effect of calling a block in the inner loop of Peter's test, which I do not in my test case (in order to solely measure the effect of the locks).

While I currently have a top notch MacBook, my results are roughly 0.16 secs for 16 queues and a loop counter of 100_000 - for incrementing a shared counter. I didn't test DispatchSemaphore because, well ... I was lazy and it shouldn't be used in those scenarios. ;)

What's interesting though, that in the case of os_unfair_lock, Swift adds an overhead (the usual refcounting management) counting to roughly 70%, and 30% is actually the time spend in os_unfair_lock. So, my guess is, C/C++/Obj-C should be about 3 times faster using os_unfair_lock. It might be even more faster using raw std::atomics, which however cannot be directly compared, because the exact usage of std::atomics depends on what you are synchronising.

Another observation is, that the uncontended case is roughly 4 times faster (i.e., 16 x 100_000 ops on a single queue).

And omitting the function call to os_unfair_lock (i.e. making it inline), would Swift enable to omit a +ref and -ref counter op per lock/unlock pair, which would make the operation much more faster. Well, the function call to os_unfair_lock is not inlineable :/

@Affaway
Copy link

Affaway commented Nov 10, 2020

Don't really understand any of this. Anyway to break it down in sinple was? Been trying to figure it out for months now but still I am lost. Thanks for your help.

@kikeenrique
Copy link

I won't recommend to use os_unfair_lock_s as in this code, PLEASE, read this before http://www.russbishop.net/the-law.
In short, in swift you must use

    var unfairLock: UnsafeMutablePointer<os_unfair_lock>
...
init
    unfairLock = UnsafeMutablePointer<os_unfair_lock>.allocate(capacity: 1)
    unfairLock.initialize(to: os_unfair_lock())
deinit
     unfairLock.deallocate()

instead of

   var unfairLock = os_unfair_lock_s()
...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment