最近,我遇到了一个奇怪的性能问题。
我需要将周期中的时间间隔与大量迭代进行比较。
我使用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毫秒(如果对周期的第二行进行了注释)。

我的问题很简短。为什么?

最佳答案

调用foo.TimeOfDay就是这样做的:

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


通过在2百万次迭代中访问2个TimeOfDay实例上的DateTime属性,您将创建400万个Timespan实例。但是,这并不是最大的花费。

进一步挖掘,您将:

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


因此,您有400万个实例化,余数计算,强制转换和&操作。这些都是便宜的操作(“便宜”当然是一个相对的术语),但数量累加。

实际的比较是微不足道的:

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


在调试模式下编译OPs代码,我看到:


空循环:4ms。
var a = firstDate > secondDate; 6毫秒(建议未将其优化掉)
var a = firstDate.TimeOfDay; 40毫秒
var a = firstDate.TimeOfDay > secondDate.TimeOfDay; 80毫秒
TimeSpan a = new TimeSpan( ticks ), b = new TimeSpan( ticks ); 7毫秒


在VS 2012上对该程序进行性能分析,有81%的样本来自DateTime.get_TimeOfDay()

运行x64,发布模式,启用所有优化:


空循环:3ms。
var a = firstDate > secondDate; 6毫秒
var a = firstDate.TimeOfDay; 20毫秒
var a = firstDate.TimeOfDay > secondDate.TimeOfDay; 40毫秒
TimeSpan a = new TimeSpan( ticks ), b = new TimeSpan( ticks ); 6毫秒


所以:


微观基准可能会产生误导(尽管并非无用)。
在确定存在问题之前启用优化。
通过优化,性能似乎翻倍。
在任何情况下,所讨论的语句似乎都没有被优化。
实例化是费用的有形但很小的一部分。
演算/算术运算占了其余的费用。
在循环之前将属性值存储在变量中可以大大提高性能。

关于c# - TimeSpan的比较慢,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14653192/

10-12 23:15