给锁跑个分

272 阅读1分钟

本篇文章是给一些常用和不常用的锁进行单线程跑分,常用的锁有信号量dispatch_semaphore@synchronized同步锁。跑分的代码来自ibireme。这里记录一下我看代码的过程以及它如何计算跑分的。

如何计算跑分的? 通过记录循环开始和循环结束的时间,然后利用结束时间减去开始时间获取耗时,耗时越少的性能越好,其中循环体是加解锁代码。

部分代码

// 锁名称枚举
typedef NS_ENUM(NSUInteger, LockType) {
    LockTypeOSSpinLock = 0,
    LockTypedispatch_semaphore,
    LockTypepthread_mutex,
    LockTypeNSCondition,
    LockTypeNSLock,
    LockTypepthread_mutex_recursive,
    LockTypeNSRecursiveLock,
    LockTypeNSConditionLock,
    LockTypesynchronized,
    LockTypeCount,
};


NSTimeInterval TimeCosts[LockTypeCount] = {0}; // c 语言的数组
int TimeCount = 0;

其中一个锁的跑分代码

    {
        OSSpinLock lock = OS_SPINLOCK_INIT;
        begin = CACurrentMediaTime();// 开机后设备一共运行了(设备休眠不统计在内)多少秒
        
        NSLog(@"begin : %f s", begin);
        
        for (int i = 0; i < count; i++) {
            OSSpinLockLock(&lock);
            OSSpinLockUnlock(&lock);
        }
        end = CACurrentMediaTime();
        
        NSLog(@"end : %f s", end);
        
        TimeCosts[LockTypeOSSpinLock] += end - begin;
        
        printf("OSSpinLock:               %8.2f ms\n", (end - begin) * 1000);
        // %8.2f\n  输出格式,按 float 定点格式,数据占8位(包括符号,小数点),小数部分两位。
        // 1 秒等于 1000 毫秒
    }

我看代码遇到的问题

  • 第一行代码就蒙了,NSTimeInterval TimeCosts[LockTypeCount] = {0};,好吧,这是一个 c 语言数组。博客签名得改名字了。
  • CACurrentMediaTime(); 是开机后设备一共运行了(设备休眠不统计在内)多少秒
  • TimeCosts[LockTypeOSSpinLock] += end - begin;先算end - begin
  • %8.2f\n 输出格式,按 float 定点格式,数据占8位(包括符号,小数点),小数部分两位。
  • 1 秒等于 1000 毫秒

结论

跑分打印结果,循环1千次

OSSpinLock:                   0.30 ms
dispatch_semaphore:           0.04 ms
pthread_mutex:                0.04 ms
NSCondition:                  0.02 ms
NSLock:                       0.03 ms
pthread_mutex(recursive):     0.04 ms
NSRecursiveLock:              0.08 ms
NSConditionLock:              0.08 ms
@synchronized:                0.13 ms
---- fin (1000) ----

循环1千万次

OSSpinLock:                  80.53 ms
dispatch_semaphore:         138.04 ms
pthread_mutex:              194.33 ms
NSCondition:                195.77 ms
NSLock:                     200.37 ms
pthread_mutex(recursive):   312.55 ms
NSRecursiveLock:            399.90 ms
NSConditionLock:            630.98 ms
@synchronized:             1009.31 ms
---- fin (10000000) ----
  • 循环次数越多OSSpinLock性能越好
  • 性能排行榜:OSSpinLock > dispatch_semaphore > pthread_mutex > NSCondition > NSLock > pthread_mutex(recursive) > NSRecursiveLock > NSConditionLock > @synchronized 。同步锁垫底了。
  • OSSpinLock 会出行优先级反转问题。OSSpinLock 是自旋锁(忙等),如果一个低优先级的线程获得锁并访问共享资源,这时一个高优先级的线程也尝试获得这个锁,它会处于忙等状态从而占用大量 CPU。此时低优先级线程无法与高优先级线程争夺 CPU 时间,从而导致任务迟迟完不成、无法释放。
  • 还是用信号量吧

相关链接

不再安全的 OSSpinLock

iOS关于时间的处理

跑分来源,如何高效跑分