std::chrono разные результаты - фиксированный временной цикл

Может ли кто-нибудь помочь мне выяснить, в чем разница? Потому что первый код:

#include <iostream>
#include <chrono>
#include <ratio>

using namespace std::chrono;

const nanoseconds timePerFrame = duration_cast<nanoseconds>(duration<steady_clock::rep, std::ratio<1, 60>>(1));
nanoseconds accumulator(0);
nanoseconds counter(0);
steady_clock::time_point begin;
int i = 0;

int main()
{
   while(true)
   {
      begin = steady_clock::now();
      while(accumulator >= timePerFrame)
      {
          accumulator -= timePerFrame;
          ++i;
      }
      accumulator += steady_clock::now() - begin;
      counter += steady_clock::now() - begin;
      if(counter >= seconds(1))
      {
        std::cout << i << std::endl;
        break;
      }
  }
}

Выходы: 30, и второй код:

#include <iostream>
#include <chrono>
#include <ratio>

using namespace std::chrono;

const nanoseconds timePerFrame = duration_cast<nanoseconds>(duration<steady_clock::rep, std::ratio<1, 60>>(1));
nanoseconds accumulator(0);
nanoseconds counter(0);
steady_clock::time_point begin;
steady_clock::time_point end;
int i = 0;

int main()
{
  while(true)
  {
      begin = steady_clock::now();
      while(accumulator >= timePerFrame)
      {
          accumulator -= timePerFrame;
          ++i;
      }
      end = steady_clock::now();
      accumulator += end - begin;
      counter += end - begin;
      if(counter >= seconds(1))
      {
        std::cout << i << std::endl;
        break;
      }
  }
}

Выходы: 60;

Единственное отличие заключается в использовании переменной end во втором примере. На мой взгляд, такой разницы не должно быть. Я имею в виду, не является ли stable_clock::now() точно таким же, как end = stable_clock::now()?

1 ответ

Решение

Разница в том, что здесь

  accumulator += steady_clock::now() - begin;
  counter += steady_clock::now() - begin;

два случая now() вернуть 2 разных значения, таким образом counter не будет в синхронизации с accumulator и следующее условие if сработает на одну итерацию раньше по сравнению с

  end = steady_clock::now();
  accumulator += end - begin;
  counter += end - begin;

потому что здесь оба, accumulator а также counter увеличиваются на ту же сумму.

Вы можете убедиться в этом, изменив порядок двух операторов на

  counter += steady_clock::now() - begin;
  accumulator += steady_clock::now() - begin;

это даст довольно непредсказуемый результат, в моем случае я получил 117.

Чтобы сделать код еще более читабельным, я бы написал так:

auto delta = end - begin;
accumulator += delta;
counter     += delta;

Всегда хорошо избегать печатать одно и то же несколько раз. В этом случае действительно имеет значение, что они увеличиваются на одинаковую величину, так почему бы не сделать это явным образом в коде?!

TL; DR steady_clock::now() такой же как end = steady_clock::now(), но steady_clock::now() не будет возвращать одно и то же значение, когда вы вызываете его дважды.

Другие вопросы по тегам