DateTime.DayOfWeek微优化 [英] DateTime.DayOfWeek micro optimization
问题描述
首先:
-
我问这个问题只是为了乐趣和渴望学习。我不得不承认我喜欢混合微型优化(尽管在任何我的发展中他们都没有导致任何显着的速度增长)。
-
DateTime.DayOfWeek
方法不代表我的任何应用程序的瓶颈。 -
它是不太可能成为任何其他问题。如果有人认为这种方法对他的应用程序的性能有影响,那么他应该考虑,
Intel®Core™i3-2350M处理器和Windows®7 Home Premium 64位。
编辑:
As Jon Skeet 注意到,这种方法可能会在不在日期边界的情况下失败。
由于Jon Skeet的评论这个答案,
dayOfWeekTurbo
可能会在不在日期边界时失败。例如,
考虑新的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的建议并应用该部门。public static DayOfWeek dayOfWeekTurbo2(this DateTime date)
{
return(DayOfWeek)((((date.Ticks>> 14)/ 52734375L)+ 1)%7);
}
另外,我们更改方法
getAllDates() / code>。
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() / code>
public static void benchDayOfWeek()
{
DateTime [] dates = getAllDates()。ToArray();
DayOfWeek [] foo = new DayOfWeek [dates.Length]; (int max_loop = 0; max_loop< 10000; max_loop ++)
{
秒表st1,st2;
st1 = Stopwatch.StartNew(); (int i = 0; i(int j = 0; j< dates.Length; j ++)
foo [j] =日期[j ] .DayOfWeek;
st1.Stop();
st2 = Stopwatch.StartNew(); (int i = 0; i(int j = 0; j< dates.Length; j ++)
foo [j] =日期[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
╔══════════════════════════════════════════ ═════════════════════════════════╗$║║║║║║║║║║║║║║║║║║║║优化DayOfWeek(2)║加速║
╠════ ════════════════════════════════════════════ ══════════════════════════╣$ b $║║║║║║║║║║║║║║║║║║║║║║║║║║║║║║║║║║║║║║║║║║ 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快。
First of all:
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).
The
DateTime.DayOfWeek
method does not represent a bottleneck in any application of mine.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 howDateTime.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:
The ticks corresponding to the current day are divided by the existing number of ticks in a day.
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.
- 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, considernew 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屋!