28.锁|性能分析

前言

是面试中较为常见的内容,在这个越来越卷的行情下,你觉得你对的知识了解的够深(卷)么?

一、 iOS的锁

1.1 经典的性能对比图

下面是一张经典的各种锁方案的性能对比图,相信很多人都见过,出自ibireme大佬。

但是这已经是多年以前的测试对比了,现如今又是什么样的结果呢?我们自己来测试一下吧!

1.2 锁的性能测试

测试代码

Swift 的方式来测试一下各个锁 10W 次加锁解锁的耗时吧,顺便熟悉下 Swift 下各种锁的用法:

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .brown
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        test_OSSpinLock()
        test_dispatch_semaphore_t()
        test_os_unfair_lock_lock()
        test_pthread_mutex_t()
        test_NSlock()
        test_NSCondition()
        test_PTHREAD_MUTEX_RECURSIVE()
        test_NSRecursiveLock()
        test_NSConditionLock()
        test_synchronized()
    }

    let excuteTimes = 100_000

    func test_OSSpinLock() {
        var lock = OS_SPINLOCK_INIT
        let start = CFAbsoluteTimeGetCurrent()
        for _ in 0..<excuteTimes {
            OSSpinLockLock(&lock)
            OSSpinLockUnlock(&lock)
        }
        let end = CFAbsoluteTimeGetCurrent()
        let deta = (end - start) * 1000
        print("test_OSSpinLock: \(deta)ms")
    }

    func test_dispatch_semaphore_t() {
        let lock = DispatchSemaphore(value: 1)
        let start = CFAbsoluteTimeGetCurrent()
        for _ in 0..<excuteTimes {
            _ = lock.wait(timeout: .distantFuture)
            lock.signal()
        }
        let end = CFAbsoluteTimeGetCurrent()
        let deta = (end - start) * 1000
        print("test_dispatch_semaphore_t: \(deta)ms")
    }

    func test_os_unfair_lock_lock() {
        var lock = os_unfair_lock.init()
        let start = CFAbsoluteTimeGetCurrent()
        for _ in 0..<excuteTimes {
            os_unfair_lock_lock(&lock)
            os_unfair_lock_unlock(&lock)
        }
        let end = CFAbsoluteTimeGetCurrent()
        let deta = (end - start) * 1000
        print("test_os_unfair_lock_lock: \(deta)ms")
    }

    func test_pthread_mutex_t() {
        var lock = pthread_mutex_t()
        pthread_mutex_init(&lock, nil)
        let start = CFAbsoluteTimeGetCurrent()
        for _ in 0..<excuteTimes {
            pthread_mutex_lock(&lock)
            pthread_mutex_unlock(&lock)
        }
        let end = CFAbsoluteTimeGetCurrent()
        let deta = (end - start) * 1000
        print("test_pthread_mutex_t: \(deta)ms")
    }

    func test_NSlock() {
        let lock = NSLock()
        let start = CFAbsoluteTimeGetCurrent()
        for _ in 0..<excuteTimes {
            lock.lock()
            lock.unlock()
        }
        let end = CFAbsoluteTimeGetCurrent()
        let deta = (end - start) * 1000
        print("test_NSlock: \(deta)ms")
    }

    func test_NSCondition() {
        let lock = NSCondition()
        let start = CFAbsoluteTimeGetCurrent()
        for _ in 0..<excuteTimes {
            lock.lock()
            lock.unlock()
        }
        let end = CFAbsoluteTimeGetCurrent()
        let deta = (end - start) * 1000
        print("test_NSCondition: \(deta)ms")
    }

    func test_PTHREAD_MUTEX_RECURSIVE() {
        var mutext_recurive = pthread_mutex_t()
        var attr = pthread_mutexattr_t()
        pthread_mutexattr_init(&attr)
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)
        pthread_mutex_init(&mutext_recurive, &attr)
        let start = CFAbsoluteTimeGetCurrent()
        for _ in 0..<excuteTimes {
            pthread_mutex_lock(&mutext_recurive)
            pthread_mutex_unlock(&mutext_recurive)
        }
        let end = CFAbsoluteTimeGetCurrent()
        let deta = (end - start) * 1000
        print("test_PTHREAD_MUTEX_RECURSIVE: \(deta)ms")
    }

    func test_NSRecursiveLock() {
        let lock = NSRecursiveLock()
        let start = CFAbsoluteTimeGetCurrent()
        for _ in 0..<excuteTimes {
            lock.lock()
            lock.unlock()
        }
        let end = CFAbsoluteTimeGetCurrent()
        let deta = (end - start) * 1000
        print("test_NSRecursiveLock: \(deta)ms")
    }

    func test_NSConditionLock() {
        let lock = NSConditionLock()
        let start = CFAbsoluteTimeGetCurrent()
        for _ in 0..<excuteTimes {
            lock.lock()
            lock.unlock()
        }
        let end = CFAbsoluteTimeGetCurrent()
        let deta = (end - start) * 1000
        print("test_NSConditionLock: \(deta)ms")
    }

    func test_synchronized() {
        let start = CFAbsoluteTimeGetCurrent()
        for _ in 0..<excuteTimes {
            // Swift 的 @synchronized
            objc_sync_enter(self)
            objc_sync_exit(self)
        }
        let end = CFAbsoluteTimeGetCurrent()
        let deta = (end - start) * 1000
        print("test_synchronized: \(deta)ms")
    }
}

ARM64 架构

iPhoneX

test_OSSpinLock: 70.26898860931396ms
test_dispatch_semaphore_t: 37.284016609191895ms
test_os_unfair_lock_lock: 31.321048736572266ms
test_pthread_mutex_t: 32.119035720825195ms
test_NSlock: 33.952951431274414ms
test_NSCondition: 33.511996269226074ms
test_PTHREAD_MUTEX_RECURSIVE: 32.86397457122803ms
test_NSRecursiveLock: 34.38997268676758ms
test_NSConditionLock: 40.94898700714111ms
test_synchronized: 49.324989318847656ms

从快到慢:

  • os_unfair_lock_lock

  • pthread_mutex_t

  • PTHREAD_MUTEX_RECURSIVE

  • NSCondition

  • NSlock

  • NSRecursiveLock

  • dispatch_semaphore_t

  • NSConditionLock

  • synchronized

  • OSSpinLock

ARM64

iPhone8 模拟器下

test_OSSpinLock: 37.75501251220703ms
test_dispatch_semaphore_t: 39.17098045349121ms
test_os_unfair_lock_lock: 37.19806671142578ms
test_pthread_mutex_t: 35.0489616394043ms
test_NSlock: 37.10508346557617ms
test_NSCondition: 34.54601764678955ms
test_PTHREAD_MUTEX_RECURSIVE: 32.772064208984375ms
test_NSRecursiveLock: 34.829020500183105ms
test_NSConditionLock: 39.30199146270752ms
test_synchronized: 39.29495811462402ms

从快到慢:

  • PTHREAD_MUTEX_RECURSIVE

  • NSCondition

  • NSRecursiveLock

  • pthread_mutex_t

  • NSlock

  • os_unfair_lock_lock

  • OSSpinLock

  • dispatch_semaphore_t

  • synchronized

  • NSConditionLock

总结

此处的结果并不能作为绝对结论。根据设备硬件及运行情况有不同,有兴趣可以自行进行调试。但总体各方案间的差距并没有到非常致命的地步。

synchronized 仍然是开发中我们最常使用的一种,性能OK、使用方便(不用操心 lock unlock)。

参考

Last updated