TimeSpan的比较太慢了
本文关键字:比较 TimeSpan | 更新日期: 2023-09-27 17:57:08
最近我遇到了一个奇怪的性能问题。我需要比较周期中的时间间隔与大量迭代。我使用 DateTime.TimeOfDay 属性来比较这些间隔。但是,我发现这些比较与日期时间比较相比非常慢。因此,我必须创建具有 1 年 1 个月和 1 天的日期时间,以加快时间间隔比较。我准备了一个小例子来说明我的意思。
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 毫秒(如果第二行为周期被评论)。
我的问题很短。为什么?
Calling foo.TimeOfDay 正在这样做:
public TimeSpan TimeOfDay
{
get
{
return new TimeSpan(this.InternalTicks % 864000000000L);
}
}
通过在 200 万次迭代中访问 2 个DateTime
实例上的 TimeOfDay
属性,您将创建 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 代码,我看到:
- 空循环:4 毫秒。
-
var a = firstDate > secondDate;
6ms(表明它没有优化) -
var a = firstDate.TimeOfDay;
40ms -
var a = firstDate.TimeOfDay > secondDate.TimeOfDay;
80ms -
TimeSpan a = new TimeSpan( ticks ), b = new TimeSpan( ticks );
7毫秒
在VS 2012中针对该程序运行性能分析,81%的样本来自DateTime.get_TimeOfDay()
。
运行 x64,发布模式,启用所有优化:
- 空循环:3 毫秒。
-
var a = firstDate > secondDate;
6 毫秒 -
var a = firstDate.TimeOfDay;
20ms -
var a = firstDate.TimeOfDay > secondDate.TimeOfDay;
40ms -
TimeSpan a = new TimeSpan( ticks ), b = new TimeSpan( ticks );
6毫秒
所以:
- 微基准测试可能会产生误导(尽管并非毫无用处)。
- 在确定存在问题之前启用优化。
- 性能似乎随着优化而翻倍。
- 有关陈述似乎在任何情况下都没有被优化。
- 实例化是费用的有形但很小的一部分。
- 铸件/算术运算占其余费用。
- 在循环之前将属性值存储在变量中可大大提高性能。
你从不使用a
,所以在第二种情况下,编译器可以优化整个语句,因为它不会产生副作用,并且永远不会使用变量。 在第一种情况下,无法确定在日期时间调用的属性不会引起副作用(优化分析不是那么深入),因此需要保留该行。
最重要的是,在确定一天中的时间时至少涉及一些计算(它需要根据一天中的刻度数修改日期时间中的刻度数),这意味着它会更慢,这只是多少的问题。