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 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 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 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 commented Oct 25, 2016

Also updated source test file

@ianbytchek

This comment has been minimized.

@Vadim-Yelagin

This comment has been minimized.

Copy link

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 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 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 :/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.