在iOS开发中,支持多种同步锁,我们从耗时角度出发,评估各种同步对象的性能。

一.同步锁

1
2
3
4
5
6
7
8
 1.@synchronized 
 2.NSLock
 3.NSCondition
 4.NSConditionLock
 5.NSRecursiveLock
 6.pthread_mutex_t
 7.OSSpinLock
 8.dispatch_barrier_async

二.性能测试示例代码

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
  const NSInteger KRunTimes = 1000 * 1000;

  double curTime, lastTime;

  // 1.同步synchronized
  id obj = [NSObject new];

  lastTime = CFAbsoluteTimeGetCurrent();
  for (NSInteger i = 0; i < KRunTimes; ++i) {
    @synchronized(obj) {
    }
  }
  curTime = CFAbsoluteTimeGetCurrent();
  NSLog(@"@synchronized: %f ms", (curTime - lastTime) * 1000);

  // 2.NSLock
  NSLock *myLock = [NSLock new];

  lastTime = CFAbsoluteTimeGetCurrent();
  for (NSInteger i = 0; i < KRunTimes; ++i) {
    [myLock lock];
    [myLock unlock];
  }
  curTime = CFAbsoluteTimeGetCurrent();
  NSLog(@"NSLock: %f ms", (curTime - lastTime) * 1000);

  // NSLock IMP
  typedef void (*func)(id, SEL);
  SEL lockSEL = @selector(lock);
  SEL unlockSEL = @selector(unlock);
  func lockFunc = (void (*)(id, SEL))[myLock methodForSelector : lockSEL];
  func unlockFunc = (void (*)(id, SEL))[myLock methodForSelector : unlockSEL];

  lastTime = CFAbsoluteTimeGetCurrent();
  for (NSInteger i = 0; i < KRunTimes; ++i) {
    lockFunc(myLock, lockSEL);
    unlockFunc(myLock, unlockSEL);
  }

  curTime = CFAbsoluteTimeGetCurrent();
  NSLog(@"NSLock + IMP: %f ms", (curTime - lastTime) * 1000);

  // 3.NSCondition
  NSCondition *condition = [[NSCondition alloc] init];
  lastTime = CFAbsoluteTimeGetCurrent();
  for (NSInteger i = 0; i < KRunTimes; ++i) {
    [condition lock];
    [condition unlock];
  }
  curTime = CFAbsoluteTimeGetCurrent();
  NSLog(@"NSCondition: %f ms", (curTime - lastTime) * 1000);

  // 4.NSConditionLock
  NSConditionLock *conditionLock = [[NSConditionLock alloc] init];
  lastTime = CFAbsoluteTimeGetCurrent();
  for (NSInteger i = 0; i < KRunTimes; ++i) {
    [conditionLock lock];
    [conditionLock unlock];
  }
  curTime = CFAbsoluteTimeGetCurrent();
  NSLog(@"NSConditionLock: %f ms", (curTime - lastTime) * 1000);

  // 5.NSRecursiveLock
  NSRecursiveLock *recursiveLock = [[NSRecursiveLock alloc] init];
  lastTime = CFAbsoluteTimeGetCurrent();
  for (NSInteger i = 0; i < KRunTimes; ++i) {
    [recursiveLock lock];
    [recursiveLock unlock];
  }
  curTime = CFAbsoluteTimeGetCurrent();
  NSLog(@"NSRecursiveLock: %f ms", (curTime - lastTime) * 1000);

  // 6.pthread_mutex_t
  pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

  lastTime = CFAbsoluteTimeGetCurrent();
  for (NSInteger i = 0; i < KRunTimes; ++i) {
    pthread_mutex_lock(&mutex);
    pthread_mutex_unlock(&mutex);
  }

  curTime = CFAbsoluteTimeGetCurrent();
  NSLog(@"pthread_mutex: %f ms", (curTime - lastTime) * 1000);
  pthread_mutex_destroy(&mutex);

  // 7.OSSpinLock 自旋锁;
  OSSpinLock spinlock = OS_SPINLOCK_INIT;
  lastTime = CFAbsoluteTimeGetCurrent();
  for (NSInteger i = 0; i < KRunTimes; ++i) {
    OSSpinLockLock(&spinlock);
    OSSpinLockUnlock(&spinlock);
  }

  curTime = CFAbsoluteTimeGetCurrent();
  NSLog(@"OSSpinlock: %f ms", (curTime - lastTime) * 1000);

  // 8 dispatch_barrier_async
  dispatch_queue_t queue =
      dispatch_queue_create("com.qq.ksnow", DISPATCH_QUEUE_CONCURRENT);

  lastTime   = CFAbsoluteTimeGetCurrent();
  for (NSInteger i = 0; i < KRunTimes; ++i) {
    dispatch_barrier_async(queue, ^{});
  }
  curTime = CFAbsoluteTimeGetCurrent();
  NSLog(@"@dispatch_barrier_async: %f ms", (curTime - lastTime) * 1000);
  

三.模拟器/iOS7/XCode6下性能对比

日志情况

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
     2014-09-07 11:26:48.071 LockTest[2713:98107] @synchronized: 232.551038 ms
    
    2014-09-07 11:26:48.173 LockTest[2713:98107] NSLock: 100.879967 ms
    2014-09-07 11:26:48.263 LockTest[2713:98107] NSLock + IMP: 89.570999 ms
    2014-09-07 11:26:48.353 LockTest[2713:98107] NSCondition: 89.850008 ms
    2014-09-07 11:26:48.587 LockTest[2713:98107] NSConditionLock: 233.431995 ms
    2014-09-07 11:26:48.677 LockTest[2713:98107] NSRecursiveLock: 89.230001 ms
    2014-09-07 11:26:48.740 LockTest[2713:98107] pthread_mutex: 62.326968 ms
    2014-09-07 11:26:48.750 LockTest[2713:98107] OSSpinlock: 10.430992 ms
    2014-09-07 11:26:49.985 LockTest[2713:98107] dispatch_barrier_async: 1234.429002 ms

模拟器环境下总结对比

image

四.iPad Mini2/iOS7/XCode6下性能对比

1
2
3
4
5
6
7
8
9
    2014-09-07 13:32:47.720 LockTest[3494:60b] @synchronized: 499.736011 ms
    2014-09-07 13:32:47.948 LockTest[3494:60b] NSLock: 227.194011 ms
    2014-09-07 13:32:48.170 LockTest[3494:60b] NSLock + IMP: 221.384048 ms
    2014-09-07 13:32:48.393 LockTest[3494:60b] NSCondition: 221.689999 ms
    2014-09-07 13:32:49.030 LockTest[3494:60b] NSConditionLock: 636.340976 ms
    2014-09-07 13:32:49.260 LockTest[3494:60b] NSRecursiveLock: 229.423046 ms
    2014-09-07 13:32:49.431 LockTest[3494:60b] pthread_mutex: 170.615971 ms
    2014-09-07 13:32:49.495 LockTest[3494:60b] OSSpinlock: 63.916981 ms
    2014-09-07 13:32:49.826 LockTest[3494:60b] dispatch_barrier_async: 329.769015 ms

image

五.总结

耗时方面

  • OSSpinlock耗时最少;
  • pthread_mutex其次。
  • NSLock/NSCondition/NSRecursiveLock 耗时接近,220ms上下居中。
  • NSConditionLock最差,我们常用synchronized倒数第二。
  • dispatch_barrier_async也许,性能并不像我们想象中的那么好.推测与线程同步调度开销有关。单独block耗时在1ms以下基本上可以忽略不计的。

原因分析

1.synchronized

会创建一个异常捕获handler和一些内部的锁。所以,使用@synchronized替换普通锁的代价是,你付出更多的时间消耗。

2.NSConditionLock

条件锁,与特定的,用户定义的条件有关。可以确保一个线程可以获取满足一定条件的锁。 内部会涉及到信号量机制,一旦一个线程获得锁后,它可以放弃锁并设置相关条件;其它线程竞争该锁。 线程之间的竞争激烈,涉及到条件锁检测,线程间通信。系统调用,上下切换方切换比较频繁。

3.OSSpinLock

自旋锁几乎不进入内核,仅仅是重新加载自旋锁。 如果自旋锁被占用时间是几十,上百纳秒,性能还是挺高的。减少了代价较高的系统调用和一系列上下文言切换。 但是,该锁不是万能的;如果该锁抢占比较多的时候,不要使用该锁。会占用较多cpu,导致耗电较多。 这种情况下使用pthread_mutex虽然耗时多一点,但是,避免了电量过多的消耗。是不错的选择。

4.pthread_mutex

底层的API还是性能比较高啊,在各种同步对象中,性能属于佼佼者。