最近,我遇到了一个奇怪的性能问题。
我需要将周期中的时间间隔与大量迭代进行比较。
我使用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/