让我们进行一些调整。
TimeSpan.TicksPerDay
(864000000000)
的质因数分解为:![Prime factorization of 864000000000](https://istack.dev59.com/4WJaN.gif)
现在可以将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();
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](https://istack.dev59.com/kgeHZ.webp)
╔══════════════════════╦════════════════════╦═════════════════════╦═════════════╗
║ 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编译,以发布模式运行,并在除应用程序外关闭所有内容(当然包括VS)。
我在东芝Satellite C660-2JK上进行了测试,
采用英特尔® Core™ i3-2350M处理器和Windows® 7 Home Premium 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](https://istack.dev59.com/6a1U8.webp)
╔══════════════════════╦════════════════════╦════════════════════════╦═════════════╗
║ 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 ║
╚══════════════════════╩════════════════════╩════════════════════════╩═════════════╝
速度提升两倍。