1

最近、奇妙なパフォーマンスの問題が1つありました。サイクル内の時間間隔を大量の反復と比較する必要があります。DateTime.TimeOfDayプロパティを使用して、これらの間隔を比較しました。ただし、これらの比較は、DateTimeの比較と比較して非常に遅いことがわかりました。そのため、時間間隔の比較を高速化するために、1年1か月1日のDateTimeを作成する必要がありました。私が何を意味するかを示すために、小さな例を用意しました。

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();
        }
    }
}

ラップトップで15ミリ秒(サイクルの最初の行がコメントされている場合)に対して176ミリ秒(サイクルの2番目の行がコメントされている場合)を取得しました。

私の質問は短いです。なんで?

4

2 に答える 2

5

を使用することはありませんa。したがって、2番目のケースでは、副作用が発生せず、変数が使用されないため、コンパイラーはステートメント全体を最適化できます。最初のケースでは、日時に呼び出されたプロパティが副作用を引き起こさないことを確認できないため(最適化分析はそれほど詳細ではありません)、線を残す必要があります。

その上、時刻の決定には少なくともいくつかの計算が含まれます(日時のティック数を1日のティック数で変更する必要があります)。これは、速度が遅くなることを意味します。いくらの質問。

于 2013-02-01T19:01:57.877 に答える
5

foo.TimeOfDay を呼び出すと、次のようになります。

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

200 万回の反復TimeOfDayで 2 つのインスタンスのプロパティにアクセスすると、 400 万のインスタンスが作成されます。ただし、それは最大の費用ではありません。DateTimeTimespan

さらに掘り下げると、次のようになります。

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. 空ループ: 4ms。
  2. var a = firstDate > secondDate;6ms (最適化されていないことを示唆)
  3. var a = firstDate.TimeOfDay;40ms
  4. var a = firstDate.TimeOfDay > secondDate.TimeOfDay;80ms
  5. TimeSpan a = new TimeSpan( ticks ), b = new TimeSpan( ticks );7ms

プログラムに対して VS 2012 でパフォーマンス分析を実行すると、サンプルの 81% がDateTime.get_TimeOfDay().

x64 の実行、リリース モード、すべての最適化が有効:

  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

そう:

  • マイクロベンチマークは誤解を招く可能性があります (役に立たないわけではありません)。
  • 問題があると判断する前に、最適化を有効にします。
  • 最適化により、パフォーマンスが 2 倍になるようです。
  • 問題のステートメントは、どのような状況下でも最適化されているようには見えません。
  • インスタンス化は、費用の具体的な部分ではありますが、わずかな部分です。
  • キャスト/算術演算は、残りの費用を占めます。
  • ループする前にプロパティ値を変数に格納すると、パフォーマンスが大幅に向上します。
于 2013-02-01T19:10:02.680 に答える