最近我遇到了一个奇怪的性能问题。 我需要比较周期中的时间间隔和大量的迭代。 我使用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毫秒(如果周期中的第二行被注释)。
我的问题很简短。为什么呢?
答案 0 :(得分:5)
你永远不会使用a
,所以在第二种情况下,编译器可以优化整个语句,因为它不会产生任何副作用,并且从不使用变量。在第一种情况下,不能确定在日期时间调用的属性不会导致副作用(优化分析不是深度),因此需要保留行。
最重要的是,在确定一天中的时间时至少需要一些计算(它需要通过一天中的刻度数来修改日期时间中的刻度数)这意味着它会变慢,这只是一个多少的问题。
答案 1 :(得分:5)
调用foo.TimeOfDay正在执行此操作:
public TimeSpan TimeOfDay
{
get
{
return new TimeSpan(this.InternalTicks % 864000000000L);
}
}
通过访问超过200万次迭代的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;
}
在调试模式下编译OP代码,我看到:
var a = firstDate > secondDate;
6毫秒(暗示它未被优化)var a = firstDate.TimeOfDay;
40ms var a = firstDate.TimeOfDay > secondDate.TimeOfDay;
80ms TimeSpan a = new TimeSpan( ticks ), b = new TimeSpan( ticks );
7ms 在VS 2012中针对该计划运行效果分析,81%的样本来自DateTime.get_TimeOfDay()
。
运行x64,发布模式,启用所有优化:
var a = firstDate > secondDate;
6ms var a = firstDate.TimeOfDay;
20ms var a = firstDate.TimeOfDay > secondDate.TimeOfDay;
40ms TimeSpan a = new TimeSpan( ticks ), b = new TimeSpan( ticks );
6ms 所以: