2013-02-01 30 views
1

最近我有一個奇怪的性能問題。 我需要比較週期中的大量迭代的時間間隔。 我使用DateTime.TimeOfDay屬性來比較這些間隔。但是,我發現這些比較與DateTime比較相比非常慢。所以,我必須創建1年1個月和1天的DateTime,以加快時間間隔比較的速度。 我準備了一個小例子來展示我的意思。TimeSpan的對比很慢

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

namespace DatesBenchmark 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Stopwatch sw = new Stopwatch(); 
      sw.Start(); 
      DateTime firstDate = DateTime.Now; 
      DateTime secondDate = DateTime.Now.AddSeconds(5); 
      for (int i = 0; i < 2000000; i++) 
      { 
       var a = firstDate.TimeOfDay > secondDate.TimeOfDay; 
       //var a = firstDate > secondDate; 
      } 
      sw.Stop(); 
      Console.WriteLine(sw.ElapsedMilliseconds); 
      Console.ReadKey(); 
     } 
    } 
} 

我爲15ms(如果在週期被註釋掉第一行)我的筆記本電腦與176毫秒(如果在週期第二行是註釋)。

我的問題很簡短。爲什麼?

+0

你爲什麼在意?超過2百萬*記錄的176毫秒毫無用處。 –

+0

這是簡化的例子。在我的真實應用程序中進行了大量的時間比較,我真的感受到了這種差異。 – eternity

+1

176ms是構建高性能例程的大量時間,而200萬條記錄不是那麼多的數據。它是否重要*是一個完全不同的問題,但這不是一個微不足道的時間或不合理的數據量。 –

回答

5

調用foo.TimeOfDay是這樣做的:

public TimeSpan TimeOfDay 
{ 
    get 
    { 
     return new TimeSpan(this.InternalTicks % 864000000000L); 
    } 
} 

通過了超過200萬次迭代要創建400萬種Timespan情況下訪問2個DateTime實例TimeOfDay財產。但是,這不是最大的開支。

進一步挖掘,你必須:

internal long InternalTicks 
{ 
    get 
    { 
     return (long)(this.dateData & 4611686018427387903uL); 
    } 
} 

所以,你有400萬個實例,其餘的計算,管型,&操作。這些都是便宜的操作(當然「便宜」是一個相對的術語),但它們的數量是相加的。

實際比較是微不足道的:

public static bool operator >(TimeSpan t1, TimeSpan t2) 
{ 
    return t1._ticks > t2._ticks; 
} 

編譯的OP代碼在調試模式下,我看到:

  1. 空循環:4毫秒。
  2. var a = firstDate > secondDate; 6ms的(這表明它不是最佳的路程)
  3. var a = firstDate.TimeOfDay; 40ms的
  4. var a = firstDate.TimeOfDay > secondDate.TimeOfDay; 80毫秒
  5. TimeSpan a = new TimeSpan(ticks), b = new TimeSpan(ticks); 7毫秒

在VS 2012運行性能分析針對該程序,樣品的81%是來自DateTime.get_TimeOfDay()

運行64位,發行模式,所有的優化啓用:

  1. 空循環:爲3ms。
  2. var a = firstDate > secondDate; 6ms的
  3. var a = firstDate.TimeOfDay; 20ms的
  4. var a = firstDate.TimeOfDay > secondDate.TimeOfDay; 40ms的
  5. TimeSpan a = new TimeSpan(ticks), b = new TimeSpan(ticks); 6ms的

所以:

  • 微基準測試可以是誤導(雖然不是沒用)。
  • 在決定出現問題之前啓用優化。
  • 性能似乎與優化加倍。
  • 在任何情況下,這些陳述似乎都沒有被優化。
  • Instantiation是費用的有形但很小的一部分。
  • 演員陣營/算術運算負責其餘的費用。
  • 循環前將屬性值存儲在變量中可以大大提高性能。
+0

鑑於'TimeSpan'是一個結構體,創建它的大量實例並不昂貴。 – Servy

+0

性能問題是,OP的測試應用程序的'TimeSpan'部分實際上正在運行,而'DateTime'比較不是。 –

+0

@Servy - 我更新了一下我的答案。另外,我同意實例化400萬個結構並不是什麼大事,但肯定比創建2個結構並比較它們要慢。 –

5

你永遠不會使用a,所以在第二種情況下,編譯器可以優化整個語句,因爲它不會導致副作用,變量也不會被使用。在第一種情況下,不能確定在日期時間調用的屬性不會導致副作用(優化分析不是深入的),因此線路需要保留。

最重要的是,至少有一些一些計算涉及確定一天中的時間(它需要修改日期時間的滴答數量在一天中的滴答數),這意味着它會要慢一點,這只是一個問題。