pthread_rwlock_ *有多贵?

时间:2015-07-24 21:15:46

标签: c++ multithreading pthreads

我只是"加速"这段代码的一部分:

double value = ComputeSomething( point );

基本上是这样的:

double value;
pthread_rwlock_rdlock( & m_Mutex );
if( ! SmallCache.Find( point, & value ) ) {
    pthread_rwlock_unlock( & m_Mutex );
    double value = ComputeSomething( point );
    pthread_rwlock_wrlock( & m_Mutex );
    SmallCache.Add( point, value );
}
pthread_rwlock_unlock( & m_Mutex );
return value;

缓存命中率约为75%。缓存是std::list; SmallCache.Findbeginend遍历它。 SmallCache.Add执行push_front个新值和pop_back个旧值,以使列表相当小,只有16个项目。 ComputeSomething被认为是中等昂贵的。

这种加速(鼓声)减速5%的结果。

因此问题:上述放缓可能是什么原因?我怀疑pthread_rwlock_*,虽然不确定。如果减速确实是由于锁定,那么人们可以避免这种情况呢?

1 个答案:

答案 0 :(得分:0)

所以,看看pthread_rwlock_rdlockpthread_rwlock_wrlock的反汇编,它在"快速情况下的合理轻量级" (也就是说,没有争用,我们可以毫无困难地接受锁定) - 大约20条指令 - 其中一条指令是cmpxchg(因此会要求任何其他处理器进入"发布这个缓存行"),但其余的都是非常简单/轻量级的指令。

当然,如果你必须等待,那么你可以稍微骑一下,但那是你所期待的。

[不,不要问我它是如何运作的]

以下是rdlock的代码,但wrlock的复杂性非常相似。

代码在这里:

  406760:   48 89 fa                mov    %rdi,%rdx
  406763:   90                      nop
  406764:   0f b6 47 20             movzbl 0x20(%rdi),%eax
  406768:   84 c0                   test   %al,%al
  40676a:   0f 8e 90 00 00 00       jle    406800 <__pthread_rwlock_rdlock+0xa0>   // May need to do stuff here

  406770:   83 e8 01                sub    $0x1,%eax
  406773:   88 47 20                mov    %al,0x20(%rdi)
  406776:   bf 01 00 00 00          mov    $0x1,%edi
  40677b:   31 c0                   xor    %eax,%eax
  40677d:   8b 72 1c                mov    0x1c(%rdx),%esi
  406780:   f0 0f b1 3a             lock cmpxchg %edi,(%rdx)
  406784:   74 16                   je     40679c <__pthread_rwlock_rdlock+0x3c>
<---- Lock maybe taken!
Else wait... 
  406786:   48 8d 3a                lea    (%rdx),%rdi
  406789:   48 81 ec 80 00 00 00    sub    $0x80,%rsp
  406790:   e8 bb 34 00 00          callq  409c50 <__lll_lock_wait>
  406795:   48 81 c4 80 00 00 00    add    $0x80,%rsp

---> Maybe taken?
  40679c:   8b 72 18                mov    0x18(%rdx),%esi
  40679f:   85 f6                   test   %esi,%esi
  4067a1:   75 4c                   jne    4067ef <__pthread_rwlock_rdlock+0x8f>
<---- Take slow path.
 Else may still have taken the lock.
  4067a3:   8b 4a 14                mov    0x14(%rdx),%ecx
  4067a6:   85 c9                   test   %ecx,%ecx
  4067a8:   75 3e                   jne    4067e8 <__pthread_rwlock_rdlock+0x88>
<--- Nope, slow path
  4067aa:   8b 4a 04                mov    0x4(%rdx),%ecx
  4067ad:   8d 41 01                lea    0x1(%rcx),%eax
  4067b0:   85 c0                   test   %eax,%eax
  4067b2:   89 42 04                mov    %eax,0x4(%rdx)
  4067b5:   0f 84 d5 00 00 00       je     406890 <__pthread_rwlock_rdlock+0x130>
 <--- Slow path, lock not taken.
  Else: Success, we have the lock, update number of times it's locked.
  4067bb:   90                      nop
  4067bc:   45 31 c0                xor    %r8d,%r8d
  4067bf:   8b 72 1c                mov    0x1c(%rdx),%esi
  4067c2:   f0 ff 0a                lock decl (%rdx)
  4067c5:   74 16                   je     4067dd <__pthread_rwlock_rdlock+0x7d>
 <---- Lock been taken, return!
ELSE if necessary, call the wake function too. 
  4067c7:   48 8d 3a                lea    (%rdx),%rdi
  4067ca:   48 81 ec 80 00 00 00    sub    $0x80,%rsp
  4067d1:   e8 1a 35 00 00          callq  409cf0 <__lll_unlock_wake>
  4067d6:   48 81 c4 80 00 00 00    add    $0x80,%rsp
--> We have the lock... 
  4067dd:   44 89 c0                mov    %r8d,%eax
  4067e0:   c3                      retq