Interlocked.Read / Interlocked.Exchange在Mono上比.NET慢得多?

时间:2012-02-07 15:57:52

标签: .net performance mono thread-safety interlocked

很抱歉这个问题很长,但有一个Jon Skeet引用,所以对某些人来说可能是值得的。

简而言之:
Interlocked.Read / Interlocked.Exchange在Mono框架中运行时的运行速度似乎比在.NET框架中运行时慢得多。我很想知道原因。

长期:
我想要一个32位平台的线程安全双,所以我做了这个结构:

public interface IThreadSafeDouble
{
    double Value { get; set; }
}

public struct LockedThreadSafeDouble : IThreadSafeDouble
{
    private readonly object Locker;
    private double _Value;

    public double Value
    {
        get { lock (Locker) return _Value; }
        set { lock (Locker) _Value = value; }
    }

    public LockedThreadSafeDouble(object init)
        : this()
    {
        Locker = new object();
    }
}

然后我读了Jon Skeet对this question的回答,所以我做了这个结构:

public struct InterlockedThreadSafeDouble : IThreadSafeDouble
{
    private long _Value;

    public double Value
    {
        get { return BitConverter.Int64BitsToDouble(Interlocked.Read(ref _Value)); }
        set { Interlocked.Exchange(ref _Value, BitConverter.DoubleToInt64Bits(value)); }
    }
}

然后我写了这个测试:

    private static TimeSpan ThreadSafeDoubleTest2(IThreadSafeDouble dbl)
    {
        var incrementTarg = 10000000;
        var sw = new Stopwatch();
        sw.Start();
        for (var i = 0; i < incrementTarg; i++, dbl.Value++);
        sw.Stop();
        return sw.Elapsed;
    }

    private static void ThreadSafeTest()
    {
        var interlockedDbl = new InterlockedThreadSafeDouble();
        var interlockedTim = ThreadSafeDoubleTest2(interlockedDbl);

        var lockedDbl = new LockedThreadSafeDouble(true);
        var lockedTim = ThreadSafeDoubleTest2(lockedDbl);

        System.Console.WriteLine("Interlocked Time: " + interlockedTim);
        System.Console.WriteLine("Locked Time:      " + lockedTim);
    }       

    public static void Main(string[] args)
    {
        for (var i = 0; i < 5; i++)
        {
            System.Console.WriteLine("Test #" + (i + 1));
            ThreadSafeTest();
        }
        System.Console.WriteLine("Done testing.");
        System.Console.ReadLine();
    }

我使用.NET框架得到了这个结果: .NET Interlocked test results

使用Mono框架的结果: Mono Interlocked test results

我在同一台机器(Windows XP)上多次运行两次测试,结果一致。我很想知道为什么Interlocked.Read/Interlocked.Exchange似乎在Mono框架上执行得那么慢。

更新

我写了以下更简单的测试:

long val = 1;
var sw = new Stopwatch();
sw.Start();
for (var i = 0; i < 100000000; i++) {
    Interlocked.Exchange(ref val, 2);
    // Interlocked.Read(ref val);
}
sw.Stop();
System.Console.WriteLine("Time: " + sw.Elapsed);

.NET框架始终使用ExchangeRead返回 ~2.5 秒。 Mono框架返回 ~5.1 秒。

1 个答案:

答案 0 :(得分:2)

绘图性能结论并不容易。在第一个例子中,长双转换可能是重要的因素。通过将所有双打更改为长(并删除转换),这些是我在Windows中32位单声道的时间:

Test #1
Interlocked Time: 00:00:01.2548628
Locked Time:      00:00:01.7281594
Test #2
Interlocked Time: 00:00:01.2466018
Locked Time:      00:00:01.7219013
Test #3
Interlocked Time: 00:00:01.2590181
Locked Time:      00:00:01.7443508
Test #4
Interlocked Time: 00:00:01.2575325
Locked Time:      00:00:01.7309012
Test #5
Interlocked Time: 00:00:01.2593490
Locked Time:      00:00:01.7528010
Done testing.

因此,互锁实施不是最重要的因素。

但是你有第二个没有转换的例子。为什么会这样?我认为答案是循环展开,在.NET JIT编译器中做得更好。但这只是猜测。如果您想比较现实生活场景中的互锁性能,您至少有两个选择:

  1. 在现实生活中比较它们。
  2. 比较JIT编译器发出的机器代码,并查看Interlocked的确切实现。
  3. 另请注意,上述实施的唯一保证是您不会发现撕裂。例如,它没有给你(通常需要的)保证,如果两个线程递增值,总和将是正确的(即它将考虑所有增量)。