8

我已经使用来自Windows Via C/C++的代码在 Windows 7 下测试了超薄读写器锁的性能。

结果让我感到惊讶的是,独占锁定性能比共享锁定性能好。这是代码和结果。

unsigned int __stdcall slim_reader_writer_exclusive(void *arg)
{
    //SRWLOCK srwLock;
    //InitializeSRWLock(&srwLock);

    for (int i = 0; i < 1000000; ++i) {
        AcquireSRWLockExclusive(&srwLock);
        g_value = 0;
        ReleaseSRWLockExclusive(&srwLock);
    }
    _endthreadex(0);
    return 0;
}

unsigned int __stdcall slim_reader_writer_shared(void *arg)
{

    int b;
    for (int i = 0; i < 1000000; ++i) {
        AcquireSRWLockShared(&srwLock);
        //b = g_value;
        g_value = 0;
        ReleaseSRWLockShared(&srwLock);
    }
    _endthreadex(0);
    return 0;
}

g_value是一个全局 int volatile 变量。

在此处输入图像描述

你能解释一下为什么会发生这种情况吗?

4

3 回答 3

22

对于小型通用锁(如 SRWLocks,它只有一个指针大小),这是一个非常常见的结果。

要点:如果你有一个非常小的受保护的代码部分,这样锁本身的开销可能占主导地位,排他锁比共享锁更适合使用。

此外,Raymond Chen 关于 g_Value 争论的论点也是正确的。如果在这两种情况下都读取而不是写入 g_Value,您可能会注意到共享锁的好处。

细节:

SRW 锁是使用单个指针大小的原子变量实现的,该变量可以采取多种不同的状态,具体取决于低位的值。这些位的使用方式的描述超出了此评论的范围——状态转换的数量非常多——所以,我将只提及您在测试中可能遇到的几个状态。

Initial lock state: (0, ControlBits:0) -- SRW 锁从所有位设置为 0 开始。

共享状态: (ShareCount: n, ControlBits: 1) -- 当没有冲突的排他获取并且锁保持共享时,共享计数直接存储在锁变量中。

独占状态: (ShareCount: 0, ControlBits: 1) -- 当没有冲突的共享获取或独占获取时,锁有一个低位设置,没有别的。

竞争状态示例: (WaitPtr:ptr, ControlBits: 3) -- 当发生冲突时,等待锁的线程使用分配在等待线程堆栈上的数据形成队列。lock 变量存储指向队列尾部的指针,而不是共享计数。

在此方案中,当您不知道初始状态时尝试获取独占锁是对锁定字的单次写入,以设置低位并检索旧值(这可以在 x86 上使用 LOCK BTS 指令完成)。如果你成功了(就像你在 1 线程情况下总是会做的那样),你可以继续进入锁定区域而无需进一步操作。

尝试获取共享锁是一个更复杂的操作:您需要首先读取锁变量的初始值以确定旧的共享计数,增加您读取的共享计数,然后使用 LOCK CMPXCHG 有条件地写回更新的值操作说明。这是一个明显较长的串行依赖指令链,因此速度较慢。此外,CMPXCGH 在许多处理器上比 LOCK BTS 等无条件原子指令要慢一些。

理论上,通过假设锁在开始时处于其初始状态并首先执行 LOCK CMPXCHG 来加速锁的第一次共享获取是可能的。这将加快锁的初始共享获取(所有这些都在您的单线程情况下),但它会显着减慢锁已被共享并且发生第二次共享获取的情况。

在释放锁时会发生一组类似的发散操作,因此管理共享状态的额外成本也在 ReleaseSRWLockShared 端支付。

于 2012-11-04T04:58:19.760 回答
6

一位致力于在 Windows 中优化锁的 Windows 内核开发人员告诉我关于性能的经验法则:

  1. 在大多数情况下使用关键部分以获得最佳性能
  2. 如果您正在阅读至少 4:1 与写作,请考虑 SRW 锁
  3. 仅当您的代码绝对不允许饥饿时才使用互斥锁

显然,关于锁还有其他方面需要考虑:

  1. 您必须清理 CS 而不必清理 SRW 锁
  2. CS 锁可以递归获取,而 SRW 锁不能
  3. 互斥锁可以同步 UM 和 KM,如果你在 KM 中,CS 和 SRW 锁不是真正的选择

所以,是的,除非阅读 >>>> 写入,否则只支持 CS。

于 2016-05-21T00:02:08.247 回答
0

推测:

排他锁是更简单的情况。共享锁支持并行性,但需要处理饥饿的可能性,因此会有额外的开销。

于 2012-11-03T05:56:50.713 回答