значение datetime.Оптимизация DayOfWeek micro
прежде всего:
Я задаю этот вопрос просто для удовольствия и хочу учиться. Я должен признать, что люблю возиться с микро-оптимизациями (хотя они никогда не приводили к значительному увеличению скорости в любом из моих разработок).
на
DateTime.DayOfWeek
метод не представляет собой узкое место в любом приложении mine.и маловероятно будет проблема в любом другом. Если кто-то думает, что этот метод влияет на производительность его приложения, он должен думать о при оптимизации и затем он должен выполнить профилирование.
Декомпиляция 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;
}
}
этот метод выполняет следующее:
тики, соответствующие текущему дню, делятся на существующие количество тиков в день.
мы добавляем 1 к предыдущему результату, чтобы остаток деления 7 находился между числами 0 и 6.
это единственный способ рассчитать день недели?
можно ли переосмыслить это, чтобы заставить его работать быстрее?
1 ответов
давайте сделаем некоторые настройки.
- премьер-факторизации
TimeSpan.TicksPerDay
(864000000000)
:
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();
// 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 ║
╚══════════════════════╩════════════════════╩═════════════════════╩═════════════╝
3x быстрее.
Примечание: код был скомпилирован с помощью Visual Studio 2013 с, Режим выпуска, и побежал со всем закрытым, кроме приложения. (Включая VS, конечно).
я провел тесты в Спутник toshiba C660-2JK, Процессор Intel® Core™ i3-2350M и 64-разрядная версия Windows® 7 Home Premium.
EDIT:
As Джон Скит замечено, что этот метод может завершиться ошибкой, если он не находится на границе даты.
из-за комментария Джона Скита этот ответ,
dayOfWeekTurbo
может произойти сбой, когда он не находится на границе даты. Например, считатьnew DateTime(2014, 3, 11, 21, 39, 30)
- ваш метод думает сегодня пятница, а на самом деле вторник. "Мы работаем по модулю - В принципе, это неправильный путь... удалив это дополнительное деление, день недели меняется в течение дня.
я решил отредактировать его.
если мы изменим 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);
}
}
не удается!
Джон Скит право. Давайте последуем совету Джона Скита и применим разделение.
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 ║
╚══════════════════════╩════════════════════╩════════════════════════╩═════════════╝
в 2 раза быстрее.