TimeSpan的比较非常缓慢

ete*_*ity 1 .net c# performance

最近我有一个奇怪的性能问题.我需要比较周期中的时间间隔和大量的迭代.我使用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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我的笔记本电脑上有15毫秒(如果循环中的第一行被评论)与176毫秒(如果循环中的第二行被评论).

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

Ser*_*rvy 5

你永远不会使用a,所以在第二种情况下,编译器可以优化整个语句,因为它不会产生副作用,并且从不使用变量.在第一种情况下,不能确定在日期时间调用的属性不会导致副作用(优化分析不是深度),因此需要保留行.

最重要的是,至少有一些计算涉及确定一天中的时间(它需要通过一天中的刻度数来修改日期时间中的刻度数),这意味着它会更慢,它只是一个多少问题.


Tim*_*ora 5

调用foo.TimeOfDay正在这样做:

public TimeSpan TimeOfDay
{
    get
    {
        return new TimeSpan(this.InternalTicks % 864000000000L);
    }
}
Run Code Online (Sandbox Code Playgroud)

通过TimeOfDayDateTime超过200万次迭代的2个实例上访问该属性,您将创建400万个Timespan实例.然而,这不是最大的开支.

进一步挖掘,你有:

internal long InternalTicks
{
    get
    {
        return (long)(this.dateData & 4611686018427387903uL);
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,您有400万个实例化,余数计算,强制转换和&操作.这些都是廉价的操作("便宜"当然是一个相对的术语),但他们加起来的数量.

实际比较是微不足道的:

public static bool operator >(TimeSpan t1, TimeSpan t2)
{
    return t1._ticks > t2._ticks;
}
Run Code Online (Sandbox Code Playgroud)

在Debug模式下编译OP代码,我看到:

  1. 空循环:4ms.
  2. var a = firstDate > secondDate; 6毫秒(暗示它没有优化)
  3. var a = firstDate.TimeOfDay; 40毫秒
  4. var a = firstDate.TimeOfDay > secondDate.TimeOfDay; 80毫秒
  5. TimeSpan a = new TimeSpan( ticks ), b = new TimeSpan( ticks ); 7毫秒

在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; 40毫秒
  5. TimeSpan a = new TimeSpan( ticks ), b = new TimeSpan( ticks ); 6ms的

所以:

  • 微基准测试可能具有误导性(尽管不是无用的).
  • 在确定存在问题之前启用优化.
  • 性能似乎是优化的两倍.
  • 在任何情况下,有关陈述似乎都没有得到优化.
  • 实例化是费用的有形但很小的一部分.
  • 演员/算术运算占其余费用.
  • 在循环之前将属性值存储在变量中可以极大地提高性能.