2012-02-07 71 views
3

對不起,有一個長期的問題,但有一個喬恩Skeet參考,所以它可能是值得的一些。Interlocked.Read/Interlocked.Exchange在Mono比.NET慢很多?

簡而言之:
Interlocked.Read/Interlocked.Exchange似乎在單聲道框架比而在.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(); 
    } 
} 

然後我讀喬恩斯基特的回答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 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框架始終返回〜2.5秒既ExchangeRead。 Mono框架返回〜5.1秒。

+1

我從「這個測試不是很有用」開始:在Windows上運行.NET 4.0 64位,在Linux上運行Mono來比較當前版本的性能。 – skolima 2012-02-07 16:15:34

+1

您實際上並不知道它是否是聯鎖方法較慢,它可能也是接口調度。我建議在得出任何結論之前儘可能多地去除泛化(即將聯鎖方法放在循環中)。 – 2012-02-07 21:18:58

+0

@RolfBjarneKvinge - 我用一個更簡單的測試和結果更新了我的問題。 – ken 2012-02-08 21:44:32

回答

2

繪圖性能結論並非如此簡單。在第一個例子中,長整數< - >雙重轉換可能是重要的因素。通過改變所有雙打多頭(和刪除轉換),這些都是我在32位單次在Windows:

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的確切實現。

另請注意,上述實施方案給出的唯一保證是您不會看到撕裂。例如,它不會給你(通常需要的)保證,如果兩個線程正在遞增值,則總和將是正確的(即,它將考慮所有增量)。