Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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

This comment has been minimized.

Copy link
Owner Author

@steipete 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

This comment has been minimized.

Copy link

@Pearapps Pearapps commented Oct 10, 2016

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

This comment has been minimized.

Copy link
Owner Author

@steipete steipete commented Oct 11, 2016

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

This comment has been minimized.

Copy link

@RomanTruba RomanTruba commented Oct 25, 2016

Also updated source test file

@iby

This comment has been minimized.

@Vadim-Yelagin

This comment has been minimized.

Copy link

@Vadim-Yelagin Vadim-Yelagin commented Oct 27, 2018

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

This comment has been minimized.

Copy link

@drewster99 drewster99 commented Mar 6, 2019

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

@couchdeveloper

This comment has been minimized.

Copy link

@couchdeveloper 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

This comment has been minimized.

Copy link

@Affaway 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.

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