DateTime.DayOfWeek微优化

首先:

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

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

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

用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之间。

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

为了使其运行更快,是否可以重新实现?

我们来做一些调整。

  1. TimeSpan.TicksPerDay (864000000000)分解: 总理分解864000000000

DayOfWeek现在可以表示为:

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

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

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

直观地说,它工作。 但让我们用代码来certificate它

 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 

如果我们用数据制作图表,看起来像这样:

图表

 ╔══════════════════════╦════════════════════╦═════════════════════╦═════════════╗ ║ 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模式编译的,并且在closures所有应用程序的情况下运行。 (当然包括VS)。

我在东芝Satellite C660-2JK ,英特尔®酷睿™i3-2350M处理器和Windows®7家庭高级版64位运行testing。

编辑:

Jon Skeet注意到,这种方法在不在date边界时可能会失败。

由于Jon Skeet评论这个答案,

dayOfWeekTurbo不在date边界时可能会失败。 例如,考虑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的build议来应用这个部门。

 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 

图表

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

快两倍