выполните первую итерацию в цикле с той же скоростью, что и остальные

#c#

#c#

Вопрос:

Я пытаюсь что-то сравнить.

У меня есть цикл, подобный

 for (int i = 1; i <= 1000; i  )
{
    Thing thing = createThing(i);

    DateTime startTime = DateTime.Now;
    thing.ComputationallyExpensiveOp();
    TimeSpan elapsed = DateTime.Now - startTime;

    Console.WriteLine(String.Format("i = "   i   "ttime = "   elapsed.TotalMilliseconds);
}
  

Похоже, что первая итерация, i = 1 , занимает значительное количество времени дольше, чем следовало бы (на несколько порядков), исходя из того, сколько времени требуется для завершения остальных.

Вторая итерация также часто кажется слишком длинной, хотя и менее очевидной.

Я чувствую, что это потому, что цикл вызывает кэширование большого количества значений, которые не были настроены на первой итерации.

Есть ли способ сделать первую итерацию i = 1 такой же «быстрой» (с точки зрения накладных расходов), как и остальные, чтобы я действительно только рассчитывал время (как можно лучше) thing.ComputationallyExpensiveOp() .

На данный момент очевидно, что первая итерация не является точным отражением thing.ComputationallyExpensiveOp() .

Я уже пытался переместить инициализацию «прогрева» над циклом, но это не сработало.

 Thing thing = createThing(1);
thing.ComputationallyExpensiveOp();

for (int i = 1; i <= 1000; i  )
{
    thing = createThing(i);

    DateTime startTime = DateTime.Now;
    thing.ComputationallyExpensiveOp();
    TimeSpan elapsed = DateTime.Now - startTime;

    Console.WriteLine(String.Format("i = "   i   "ttime = "   elapsed.TotalMilliseconds);
}
  

Комментарии:

1. Какую «инициализацию прогрева» вы пробовали, но не сработало? Обычно вызов кода, который должен быть протестирован, работает

2. @harold Я отредактировал, чтобы показать. Я собираюсь протестировать копирование также Conole.WriteLine и таймингов и т.д.

3. Вероятно, просто время циклов x и выведите медиану или среднее значение партии. Поскольку звучит так, как будто вы не можете контролировать кэширование, происходящее в createThing()

4. Как мы можем разумно ответить на это, не зная, что происходит в CreateThing и ComputationallyExpensiveOp? Какое кэширование присутствует в этих двух методах?

5. Я скопировал все тело цикла над циклом, и, похоже, это «исправлено». Ofc Я не могу быть уверен, но цифры выглядят намного больше, чем ожидалось сейчас.

Ответ №1:

Я использую такие for циклы десятилетиями. И я никогда не сталкивался с тем, что такой for цикл был причиной задержки во время первой итерации.

Я ничего не знаю о вашей Thing реализации, но я совершенно уверен, что причина вашей задержки кроется именно в этом. Не в цикле.

Комментарии:

1. Вероятно, это ни то, ни другое, но просто в C # первый вызов любого метода происходит медленно, потому что код еще не был JIT-скомпилирован (поэтому время для его JIT-компиляции включено в тест, если вы вызываете холодный код)

2. Верно, но если ComputationallyExpensiveOp в OP на самом деле не так дорого с точки зрения вычислений, компиляция JIT не объясняет «на несколько порядков» более длительное выполнение первой итерации в этом цикле… Я понимаю, что компиляция JIT сопряжена с накладными расходами, но, как я уже сказал, я никогда не сталкивался с такой задержкой, вызванной моим компилятором / средой за всю мою карьеру. Это всегда в первую очередь было связано с деталями реализации разработанной мной логики.

Ответ №2:

Копирование всего тела цикла снаружи «исправило» это для меня

 Thing thing = createThing(1);

DateTime startTime = DateTime.Now;
thing.ComputationallyExpensiveOp();
TimeSpan elapsed = DateTime.Now - startTime;

Console.WriteLine(String.Format("i = "   1   "ttime = "   elapsed.TotalMilliseconds);

for (int i = 1; i <= 1000; i  )
{
    thing = createThing(i);

    startTime = DateTime.Now;
    thing.ComputationallyExpensiveOp();
    elapsed = DateTime.Now - startTime;

    Console.WriteLine(String.Format("i = "   i   "ttime = "   elapsed.TotalMilliseconds);
}
  

Я не совсем уверен в причине, но теперь время выполнения первой итерации больше похоже на ожидаемое.

Комментарии:

1. Я рад, что это решение сработало для вас. Однако это остается странной проблемой. Объявление трех переменных вне for цикла не должно приводить к заметному увеличению производительности, особенно если thing.ComputationallyExpensiveOp() вызов будет потреблять большую часть общей вычислительной мощности вашей логики. Если у вас будет такая возможность, я бы посоветовал вам продолжить изучение этой проблемы. Если истинное происхождение проблемы остается нераскрытым, вы можете столкнуться со «спонтанным» ее повторным появлением рано или поздно. И к тому времени это может быть сложнее определить и решить.

2. @BartHofland да, я тоже подумал, что это странно, к счастью, это не часть чего-либо, что я буду использовать повторно в будущем, но я мог бы исследовать это снова через несколько дней, чтобы обновить этот пост SO