【问题标题】:Spinlock.exit with or without memory barrierSpinlock.exit 有或没有内存屏障
【发布时间】:2013-06-06 13:38:45
【问题描述】:

由于内存屏障对我来说是一个新概念,所以我试图理解它们,所以我编写了以下测试程序 (C#):

private static void Func1()
{
    SpinLock sl = new SpinLock();

    Action action = () =>
    {
        for (int i = 0; i < 100; i++)
        {
            bool lockTaken = false;
            sl.Enter(ref lockTaken);
            if (lockTaken)
                sl.Exit(true);
        }
    };

    Parallel.Invoke(action, action);
}

private static void Main(string[] arg)
{
    for (int i = 0; i < 10000; i++)
        Func1();
}

问题是关于 Spinlock.Exit(true) 函数。 true 表示发出内存屏障以立即将退出操作发布到其他线程。

当传递 false 时,不会发出内存屏障,然后代码运行速度几乎是原来的两倍。

如果动作对象包含线程之间的共享内存并且传递了 false,它仍然是一个正确的程序吗? 为什么内存屏障这么慢?

【问题讨论】:

    标签: c# .net memory-barriers


    【解决方案1】:

    Exit(true) 或 Exit(false) 的代码都是正确的,即使在 IA64 上也是如此。布尔参数concerns fairness。它只是碰巧实现使用内存屏障指令来获得这种公平性。

    小心从这类事情的琐碎基准中得出结论。通常,微不足道的基准会使不公平的解决方案看起来像一个不折不扣的赢家。但是在更大的系统的上下文中,缺乏公平性有时会通过饿死试图取得进展的线程之一来造成重大损害。但有时不公平会更好。这取决于上下文。

    【讨论】:

    • 它并不是一个真正的基准,我只是想弄清楚为什么它慢了一倍。仅仅是因为发出了额外的指令,还是在执行屏障指令之前内核何时全部停止?
    • 你在什么样的硬件上运行?
    • 内存屏障不会停止任何其他核心,但会以多种方式影响其执行的核心。它会导致所有先前的指令在任何后续指令开始之前完成执行并将其结果存储到 L1 缓存中。如果没有内存屏障,指令可以并发或乱序执行,甚至可以绕过存储到 L1。 intel.com/content/www/us/en/architecture-and-technology/… 的第 2.3.3 节提供了有关系统管道的更多详细信息。
    • 有关英特尔处理器上的自旋锁和公平性的详细讨论,请参阅intel.com/content/dam/www/public/us/en/documents/white-papers/…
    猜你喜欢
    • 2018-02-28
    • 1970-01-01
    • 2014-02-04
    • 1970-01-01
    • 1970-01-01
    • 2018-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多