我只是"加速"这段代码的一部分:
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.Find
从begin
到end
遍历它。 SmallCache.Add
执行push_front
个新值和pop_back
个旧值,以使列表相当小,只有16个项目。 ComputeSomething
被认为是中等昂贵的。
这种加速(鼓声)减速5%的结果。
因此问题:上述放缓可能是什么原因?我怀疑pthread_rwlock_*
,虽然不确定。如果减速确实是由于锁定,那么人们可以避免这种情况呢?
答案 0 :(得分:0)
所以,看看pthread_rwlock_rdlock
和pthread_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