DateTime.DayOfWeek微优化

时间:2014-03-07 18:38:06

标签: c# performance datetime micro-optimization dayofweek

首先:

  1. 我问这个问题只是为了好玩和渴望学习。我不得不承认我喜欢搞微观优化(虽然他们从未在我的任何开发中导致任何显着的速度提升)。

  2. DateTime.DayOfWeek方法并不代表我的任何应用程序的瓶颈。

  3. 非常不可能成为其他任何问题。如果有人认为这种方法对他的应用程序的性能有影响, 他应该考虑When to optimize然后,他应该进行分析。

  4. 使用ILSpy反编译DateTime类,我们了解DateTime.DayOfWeek的实现方式:

    [__DynamicallyInvokable]
            public DayOfWeek DayOfWeek
            {
                [__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
                get
                {
                    return (DayOfWeek)((this.InternalTicks / 864000000000L + 1L) % 7L);
                }
            }
    
    
    public long Ticks
    {
        [__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
        get
        {
            return this.InternalTicks;
        }
    }
    

    此方法执行以下操作:

    1. 与当天对应的刻度除以一天中现有的刻度数。

    2. 我们在前面的结果中加1,以便7的除法余数在0到6之间。

    3. 这是计算星期几的唯一方法吗?

      是否有可能重新实现这一目标以使其运行得更快?

1 个答案:

答案 0 :(得分:72)

让我们做一些调整。

  1. TimeSpan.TicksPerDay (864000000000)Prime factorization of 864000000000
  2. 的素数分解

    DayOfWeek现在可以表示为:

    public DayOfWeek DayOfWeek
    {                   
        get
        {
            return (DayOfWeek)(((Ticks>>14) / 52734375 + 1L) % 7L);
        }
    }
    

    我们正在使用模7,52734375 % 7它是1.所以,上面的代码等于:

    public static DayOfWeek dayOfWeekTurbo(this DateTime date)
    {
        return (DayOfWeek)(((date.Ticks >> 14) + 1) % 7);
    }
    

    直观地说,它有效。但是我们用代码

    来证明它
    public static void proof()
    {
        DateTime date = DateTime.MinValue;
        DateTime max_date = DateTime.MaxValue.AddDays(-1);
        while (date < max_date)
        {
            if (date.DayOfWeek != date.dayOfWeekTurbo())
            {
                Console.WriteLine("{0}\t{1}", date.DayOfWeek, date.dayOfWeekTurbo());
                Console.ReadLine();
            }
            date = date.AddDays(1);
        }
    }
    

    如果你愿意,你可以运行它,但我保证你工作正常。

    好的,唯一剩下的就是一些基准测试。

    这是一种辅助方法,以使代码更清晰:

    public static IEnumerable<DateTime> getAllDates()
    {
        DateTime d = DateTime.MinValue;
        DateTime max = DateTime.MaxValue.AddDays(-1);
        while (d < max)
        {
            yield return d;
            d = d.AddDays(1);
        }
    }
    

    我想它不需要解释。

    public static void benchDayOfWeek()
    {
    
        DateTime[] dates = getAllDates().ToArray();
        // for preventing the compiler doing things that we don't want to
        DayOfWeek[] foo = new DayOfWeek[dates.Length];
        for (int max_loop = 0; max_loop < 10000; max_loop+=100)
        {
    
    
            Stopwatch st1, st2;
            st1 = Stopwatch.StartNew();
            for (int i = 0; i < max_loop; i++)
                for (int j = 0; j < dates.Length; j++)
                    foo[j] = dates[j].DayOfWeek;
            st1.Stop();
    
            st2 = Stopwatch.StartNew();
            for (int i = 0; i < max_loop; i++)
                for (int j = 0; j < dates.Length; j++)
                    foo[j] = dates[j].dayOfWeekTurbo();
            st2.Stop();
    
            Console.WriteLine("{0},{1}", st1.ElapsedTicks, st2.ElapsedTicks);
    
        }
        Console.ReadLine();
        Console.WriteLine(foo[0]);
    
    }
    

    <强>输出:

    96,28
    172923452,50884515
    352004290,111919170
    521851120,168153321
    683972846,215554958
    846791857,264187194
    1042803747,328459950
    Monday
    

    如果我们使用数据创建图表,它看起来像这样:

    Chart

    ╔══════════════════════╦════════════════════╦═════════════════════╦═════════════╗
    ║ Number of iterations ║ Standard DayOfWeek ║ Optimized DayOfWeek ║   Speedup   ║
    ╠══════════════════════╬════════════════════╬═════════════════════╬═════════════╣
    ║                    0 ║                 96 ║                  28 ║ 3.428571429 ║
    ║                  100 ║          172923452 ║            50884515 ║ 3.398351188 ║
    ║                  200 ║          352004290 ║           111919170 ║ 3.145165301 ║
    ║                  300 ║          521851120 ║           168153321 ║ 3.103424404 ║
    ║                  400 ║          683972846 ║           215554958 ║ 3.1730787   ║
    ║                  500 ║          846791857 ║           264187194 ║ 3.205272156 ║
    ║                  600 ║         1042803747 ║           328459950 ║ 3.174827698 ║
    ╚══════════════════════╩════════════════════╩═════════════════════╩═════════════╝
    

    快3倍。

    注意:代码是使用Visual Studio 2013,Release模式编译的,并且在应用程序关闭时运行。 (当然包括VS)。

    我在a toshiba Satellite C660-2JK中运行了测试, 英特尔®酷睿™i3-2350M处理器和Windows®7家庭高级版64位。

    编辑:

    当注意到Jon Skeet时,此方法在不在日期边界时会失败。

    由于Jon Skeet对此答案的评论,

      当

    dayOfWeekTurbo不在日期边界时,它会失败。例如,   考虑new DateTime(2014, 3, 11, 21, 39, 30) - 你的方法认为   这是星期五,实际上是星期二。 “我们正在以模数工作   7“是错误的方式,基本上...通过删除额外的   除法,白天的日期

    我决定编辑它。

    如果我们更改proof()方法,

    public static void proof()
    {
        DateTime date = DateTime.MinValue;
        DateTime max_date = DateTime.MaxValue.AddSeconds(-1);
        while (date < max_date)
        {
            if (date.DayOfWeek != date.dayOfWeekTurbo2())
            {
                Console.WriteLine("{0}\t{1}", date.DayOfWeek, date.dayOfWeekTurbo2());
                Console.ReadLine();
            }
            date = date.AddSeconds(1);
        }
    }
    

    失败!

    Jon Skeet是对的。 让我们按照Jon Skeet的建议来应用该部门。

    public static DayOfWeek dayOfWeekTurbo2(this DateTime date)
    {
        return (DayOfWeek)((((date.Ticks >> 14) / 52734375L )+ 1) % 7);
    }
    

    另外,我们更改方法getAllDates()

    public static IEnumerable<DateTime> getAllDates()
    {
        DateTime d = DateTime.MinValue;
        DateTime max = DateTime.MaxValue.AddHours(-1);
        while (d < max)
        {
            yield return d;
            d = d.AddHours(1);
        }
    }
    

    benchDayOfWeek()

    public static void benchDayOfWeek()
    {
    
        DateTime[] dates = getAllDates().ToArray();
        DayOfWeek[] foo = new DayOfWeek[dates.Length];
        for (int max_loop = 0; max_loop < 10000; max_loop ++)
        {
    
    
            Stopwatch st1, st2;
            st1 = Stopwatch.StartNew();
            for (int i = 0; i < max_loop; i++)
                for (int j = 0; j < dates.Length; j++)
                    foo[j] = dates[j].DayOfWeek;
            st1.Stop();
    
            st2 = Stopwatch.StartNew();
            for (int i = 0; i < max_loop; i++)
                for (int j = 0; j < dates.Length; j++)
                    foo[j] = dates[j].dayOfWeekTurbo2();
            st2.Stop();
    
            Console.WriteLine("{0},{1}", st1.ElapsedTicks, st2.ElapsedTicks);
    
        }
        Console.ReadLine();
        Console.WriteLine(foo[0]);
    
    }
    

    它还会更快吗?答案是

    <强>输出:

    90,26
    43772675,17902739
    84299562,37339935
    119418847,47236771
    166955278,72444714
    207441663,89852249
    223981096,106062643
    275440586,125110111
    327353547,145689642
    363908633,163442675
    407152133,181642026
    445141584,197571786
    495590201,217373350
    520907684,236609850
    511052601,217571474
    610024381,260208969
    637676317,275558318
    

    Chart

    ╔══════════════════════╦════════════════════╦════════════════════════╦═════════════╗
    ║ Number of iterations ║ Standard DayOfWeek ║ Optimized DayOfWeek(2) ║  Speedup    ║
    ╠══════════════════════╬════════════════════╬════════════════════════╬═════════════╣
    ║                    1 ║           43772675 ║               17902739 ║ 2.445026708 ║
    ║                    2 ║           84299562 ║               37339935 ║ 2.257624766 ║
    ║                    3 ║          119418847 ║               47236771 ║ 2.528090817 ║
    ║                    4 ║          166955278 ║               72444714 ║ 2.304588821 ║
    ║                    5 ║          207441663 ║               89852249 ║ 2.308697504 ║
    ║                    6 ║          223981096 ║              106062643 ║ 2.111781205 ║
    ║                    7 ║          275440586 ║              125110111 ║ 2.201585338 ║
    ║                    8 ║          327353547 ║              145689642 ║ 2.246923958 ║
    ║                    9 ║          363908633 ║              163442675 ║ 2.226521519 ║
    ║                   10 ║          407152133 ║              181642026 ║ 2.241508433 ║
    ║                   11 ║          445141584 ║              197571786 ║ 2.25306251  ║
    ║                   12 ║          495590201 ║              217373350 ║ 2.279903222 ║
    ║                   13 ║          520907684 ║              236609850 ║ 2.201546909 ║
    ║                   14 ║          511052601 ║              217571474 ║ 2.348895246 ║
    ║                   15 ║          610024381 ║              260208969 ║ 2.344363391 ║
    ║                   16 ║          637676317 ║              275558318 ║ 2.314124725 ║
    ╚══════════════════════╩════════════════════╩════════════════════════╩═════════════╝
    

    快2倍。