DateTime.DayOfWeek微优化 [英] DateTime.DayOfWeek micro optimization

查看:133
本文介绍了DateTime.DayOfWeek微优化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先:


  1. 我要求只是为了好玩而好学这个问题。我不得不承认我喜欢勾搭微优化(虽然他们从来没有导致我的任何发展速度的任何显著增加)。


  2. DateTime.DayOfWeek 方法不重新present在我的任何应用程序的瓶颈。


  3. 和它的不大可能的是在其他任何一个问题。如果有人在想,这种方法在他的应用程序的性能产生影响,
    他应该想想当优化然后,他应该进行分析。


反编译的DateTime 类ILSpy,我们了解如何 DateTime.DayOfWeek 实施

  [__ DynamicallyInvokable]
        公共DAYOFWEEK DAYOFWEEK
        {
            [__DynamicallyInvokable,TargetedPatchingOptOut(性能的关键跨越NGEN图像边界内联)]
            得到
            {
                回报(星期几)((this.InternalTicks / 864000000000L + 1L)%7L);
            }
        }
众长虱子
{
    [__DynamicallyInvokable,TargetedPatchingOptOut(性能关键的内联这种类型的跨NGEN图像边界法)]
    得到
    {
        返回this.InternalTicks;
    }
}

此方法执行以下操作:


  1. 对应于当前的一天蜱蜱现有数在一天内分。


  2. 我们添加1至前述结果,为了使7除法的余数为数字0和6之间。


这是计算出星期几的唯一途径?

有没有可能重新实现这一点是为了使其运行速度更快?


解决方案

让我们做一些调谐。


  1. <因式分解code> TimeSpan.TicksPerDay (8640亿)

星期几现在可以pssed如前$ P $:

 公共DAYOFWEEK DAYOFWEEK
{
    得到
    {
        回报(星期几)(((牛蜱GT;&GT; 14)/ 52734375 + 1L)%7L);
    }
}

和我们在模7个, 52734375%7 这是一所以,$ C $上面c等于:

 公共静态DAYOFWEEK dayOfWeekTurbo(这个日期为准​​)
{
    返回(星期几)(((date.Ticks&GT;&GT; 14)+ 1)%7);
}

直观地说,它的工作原理。但是,让我们证明这一点的与code

 公共静态无效的证明()
{
    日期时间日期= DateTime.MinValue;
    日期时间MAX_DATE = DateTime.MaxValue.AddDays(-1);
    而(日期&LT; MAX_DATE)
    {
        如果(date.DayOfWeek!= date.dayOfWeekTurbo())
        {
            Console.WriteLine({0} \\ t {1},date.DayOfWeek,date.dayOfWeekTurbo());
            到Console.ReadLine();
        }
        日期= date.AddDays(1);
    }
}

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

好了,唯一剩下的东西是有点标杆。

这是一种辅助方法中,为了使code更清楚:

 公共静态的IEnumerable&LT;&日期时间GT; getAllDates()
{
    日期时间D = DateTime.MinValue;
    日期时间最大= DateTime.MaxValue.AddDays(-1);
    而(D&LT;最大)
    {
        产生回报D组;
        D = d.AddDays(1);
    }
}

我想这不需要解释。

 公共静态无效benchDayOfWeek()
{    。DateTime的[]日期= getAllDates()ToArray的();
    //为preventing编译器做的事情,我们不希望
    DAYOFWEEK []富=新DAYOFWEEK [dates.Length]
    对于(INT max_loop = 0; max_loop&LT; 10000; max_loop + = 100)
    {
        秒表ST1,ST2;
        ST1 = Stopwatch.StartNew();
        的for(int i = 0; I&LT; max_loop;我++)
            对于(INT J = 0; J&LT; dates.Length; J ++)
                富[J] =日期[J] .DayOfWeek;
        st1.Stop();        ST2 = Stopwatch.StartNew();
        的for(int i = 0; I&LT; max_loop;我++)
            对于(INT J = 0; J&LT; dates.Length; J ++)
                富[J] =日期[J] .dayOfWeekTurbo();
        st2.Stop();        Console.WriteLine({0},{1},st1.ElapsedTicks,st2.ElapsedTicks);    }
    到Console.ReadLine();
    Console.WriteLine(富[0]);}

输出:

  96,28
172923452,50884515
352004290,111919170
521851120,168153321
683972846,215554958
846791857,264187194
1042803747,328459950
星期一

如果我们做与数据的图表,它看起来像这样:

<$p$p><$c$c>╔══════════════════════╦════════════════════╦═════════════════════╦═════════════╗
║║迭代标准DAYOFWEEK║优化DAYOFWEEK║提速数║
╠══════════════════════╬════════════════════╬═════════════════════╬═════════════╣
║║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倍速度更快。

注:code编译使用Visual Studio 2013,发布模式,并与一切关闭,但应用程序运行。 (包括VS,当然)。

我跑一个东芝Satellite C660-2JK
英特尔®酷睿™处理器的睿i3-2350M,以及Windows®7家庭premium 64位。

编辑:

由于乔恩斯基特的注意到了,这种方法可以当它是不是约会边界上失败。

由于乔恩斯基特的评论这个答案,


  

dayOfWeekTurbo 时,它不是一个日期边界上可能会失败。例如,
  考虑(2014年30,3,11,21,39,) 新的日期时间 - 你认为法
  今天是星期五,当它实际上是星期二。在我们是在模工作
  7是南辕北辙,基本上......通过去除额外的
  师,某一天的一周的变化,白天的


我决定来编辑它。

如果我们改变了证明()

 公共静态无效的证明()
{
    日期时间日期= DateTime.MinValue;
    日期时间MAX_DATE = DateTime.MaxValue.AddSeconds(-1);
    而(日期&LT; MAX_DATE)
    {
        如果(date.DayOfWeek!= date.dayOfWeekTurbo2())
        {
            Console.WriteLine({0} \\ t {1},date.DayOfWeek,date.dayOfWeekTurbo2());
            到Console.ReadLine();
        }
        日期= date.AddSeconds(1);
    }
}

失败!

乔恩斯基特是正确的。
让我们跟随乔恩斯基特的意见和应用部门。

 公共静态DAYOFWEEK dayOfWeekTurbo2(这个日期为准​​)
{
    返回(星期几)((((date.Ticks&GT;&GT; 14)/ 52734375L)+ 1)%7);
}

此外,我们改变方法 getAllDates()

 公共静态的IEnumerable&LT;&日期时间GT; getAllDates()
{
    日期时间D = DateTime.MinValue;
    日期时间最大= DateTime.MaxValue.AddHours(-1);
    而(D&LT;最大)
    {
        产生回报D组;
        D = d.AddHours(1);
    }
}

benchDayOfWeek()

 公共静态无效benchDayOfWeek()
{    。DateTime的[]日期= getAllDates()ToArray的();
    DAYOFWEEK []富=新DAYOFWEEK [dates.Length]
    对(INT max_loop = 0; max_loop&下; 10000; max_loop ++)
    {
        秒表ST1,ST2;
        ST1 = Stopwatch.StartNew();
        的for(int i = 0; I&LT; max_loop;我++)
            对于(INT J = 0; J&LT; dates.Length; J ++)
                富[J] =日期[J] .DayOfWeek;
        st1.Stop();        ST2 = Stopwatch.StartNew();
        的for(int i = 0; I&LT; max_loop;我++)
            对于(INT J = 0; J&LT; dates.Length; J ++)
                富[J] =日期[J] .dayOfWeekTurbo2();
        st2.Stop();        Console.WriteLine({0},{1},st1.ElapsedTicks,st2.ElapsedTicks);    }
    到Console.ReadLine();
    Console.WriteLine(富[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

<$p$p><$c$c>╔══════════════════════╦════════════════════╦════════════════════════╦═════════════╗
║迭代次数║标准DAYOFWEEK║优化DAYOFWEEK(2)║║提速
╠══════════════════════╬════════════════════╬════════════════════════╬═════════════╣
║║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倍。

First of all:

  1. I'm asking this question just for fun and eager to learn. I have to admit I love to mess around with micro-optimizations (Although they have never led to any significant increase in speed in any of my developments).

  2. The DateTime.DayOfWeek method does not represent a bottleneck in any application of mine.

  3. And it is highly unlikely to be a problem in any other. If anyone is thinking that this method has an impact on the performance of his application, he should think about When to optimize and then, he should perform a profiling.

Decompiling DateTime class with ILSpy, we find out how DateTime.DayOfWeek is implemented:

[__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;
    }
}

This method performs the following:

  1. The ticks corresponding to the current day are divided by the existing number of ticks in a day.

  2. We add 1 to the foregoing result, in order that the remainder of division of 7 is between the numbers 0 and 6.

Is this the only way to calculate the day of the week?

Would it be possible to reimplement this in order to make it run faster?

解决方案

Let's do some tunning.

  1. Prime factorization of TimeSpan.TicksPerDay (864000000000) :

DayOfWeek now can be expressed as:

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

And we are working in modulo 7, 52734375 % 7 it's 1. So, the code above is equal to:

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

Intuitively, it works. But let's prove it with code

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

You can run it if you want, but I assure you it works fine.

Ok, the only thing left is a bit of benchmarking.

This is an auxiliary method, in order to make the code clearer:

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

I guess it needs no explanation.

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

}

Output:

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

If we make a chart with the data, it looks like this:

╔══════════════════════╦════════════════════╦═════════════════════╦═════════════╗
║ 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 ║
╚══════════════════════╩════════════════════╩═════════════════════╩═════════════╝

3x faster.

Note: the code was compiled with Visual Studio 2013, Release mode, and ran with everything closed but the application. (Including VS, of course).

I ran the tests in a toshiba Satellite C660-2JK, Intel® Core™ i3-2350M Processor, and Windows® 7 Home Premium 64-bit.

EDIT:

As Jon Skeet noticed, this method can fail when it's not on a date boundary.

Due to Jon Skeet's comment this answer,

dayOfWeekTurbo can fail when it's not on a date boundary. For example, consider new DateTime(2014, 3, 11, 21, 39, 30) - your method thinks it's Friday when actually it's Tuesday. The "we are working in modulo 7" is the wrong way round, basically... by removing that extra division, the day-of-week changes during the day.

I decided to edit it.

If we change the proof() method,

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

Fails!

Jon Skeet was right. Let's follow Jon Skeet's advice and apply the division.

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

Also, we change the method 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);
    }
}

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

}

It will still be faster? the answer is yes

Output:

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

╔══════════════════════╦════════════════════╦════════════════════════╦═════════════╗
║ 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 ║
╚══════════════════════╩════════════════════╩════════════════════════╩═════════════╝

2x faster.

这篇关于DateTime.DayOfWeek微优化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆