DateTime.DayOfWeek微优化

rpa*_*pax 20 c# performance datetime dayofweek micro-optimization

首先:

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

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

  3. 并且它不太可能 成为任何其他问题.如果有人认为这种方法对他的应用程序的性能有影响,他应该考虑何时进行优化,然后,他应该进行分析.

DateTime使用ILSpy 反编译类,我们了解如何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;
    }
}
Run Code Online (Sandbox Code Playgroud)

此方法执行以下操作:

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

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

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

是否有可能重新实现它以使其运行更快?

rpa*_*pax 77

我们来做一些调整.

  1. 主要因素分解:TimeSpan.TicksPerDay (864000000000)素数因子化为864000000000

DayOfWeek 现在可以表达为:

public DayOfWeek DayOfWeek
{                   
    get
    {
        return (DayOfWeek)(((Ticks>>14) / 52734375 + 1L) % 7L);
    }
}
Run Code Online (Sandbox Code Playgroud)

我们在模7中工作,52734375 % 7它是1.所以,上面的代码等于:

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

直观地说,它有效.但是,让我们用代码来证明这一点

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我想它不需要解释.

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]);

}
Run Code Online (Sandbox Code Playgroud)

输出:

96,28
172923452,50884515
352004290,111919170
521851120,168153321
683972846,215554958
846791857,264187194
1042803747,328459950
Monday
Run Code Online (Sandbox Code Playgroud)

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

图表

?????????????????????????????????????????????????????????????????????????????????
? 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 ?
?????????????????????????????????????????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

快3倍.

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

我在东芝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);
    }
}
Run Code Online (Sandbox Code Playgroud)

失败!

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

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

另外,我们改变了方法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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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]);

}
Run Code Online (Sandbox Code Playgroud)

它会更快吗?答案是肯定的

输出:

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
Run Code Online (Sandbox Code Playgroud)

图表

????????????????????????????????????????????????????????????????????????????????????
? 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 ?
????????????????????????????????????????????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

快2倍.

  • 当dayOfWeekTurbo`不在日期边界时,它会失败.例如,考虑`new DateTime(2014,3,11,21,39,30)` - 你的方法认为它是星期五,实际上它是星期二."我们正在以模7工作"是错误的方式,基本上......通过删除额外的划分,白天的星期几变化*. (10认同)
  • 神圣的废话,你怎么没有更多的upvotes.很好的答案. (7认同)
  • @AdamSears我发现它[这里](http://www.sensefulsolutions.com/2010/10/format-text-as-table.html).它在我的书签栏上:) (4认同)