std::chrono::high_resolution_clock таймер кадра на основе

Я уже много лет использую следующее определение часов для таймера кадра:

    using frame_clock = std::conditional_t<
        std::chrono::high_resolution_clock::is_steady,
        std::chrono::high_resolution_clock,
        std::chrono::steady_clock>;

Другими словами, мне нужны часы, определяемые с максимально возможным разрешением, но они должны увеличиваться монотонно. Обратите внимание, что MSVC в настоящее время использует следующий псевдоним для определения std::chrono::high_resolution_clock:

    using high_resolution_clock = steady_clock;

Поэтому в MSVC определенный мной псевдоним будет просто использовать std::chrono::steady_clock. Это не обязательно верно для libstdc++ и libc++, отсюда и использование псевдонима.

Недавно я наткнулся здесь на сноску: https://en.cppreference.com/w/cpp/chrono/high_resolution_clock

Обратите внимание, что cppreference явно не рекомендует использовать std::chrono::high_resolution_clock. Их обоснование заключалось в том, что часы различаются в зависимости от реализации... но разве это не верно и для std::chrono::steady_clock и std::chrono::system_clock? Например, я не смог найти ничего, что гарантировало бы, что периоды часов между часами должны быть в определенных единицах. На самом деле, я понимаю, что это преднамеренно.

Мой вопрос в том, что после стольких лет использования std::chrono::high_resolution_clock (для таймеров кадров и тестов), должен ли я беспокоиться больше, чем сейчас? Даже здесь, на этом сайте, я вижу много рекомендаций по использованию std::chrono::high_resolution_clock, несмотря на то, что говорится в этой сноске. Любое дальнейшее понимание этого несоответствия или примеры того, где это может вызвать проблемы, будут высоко оценены.


person Christopher Mauer    schedule 23.04.2021    source источник
comment
Я думаю, возникает вопрос: для чего вы используете frame_clock?   -  person YSC    schedule 23.04.2021
comment
Это таймер кадра. Поэтому он используется в приложениях, где обработка должна быть разделена на кадры. Например, встроенные приложения реального времени, где вся обработка должна умещаться в кадре с частотой 3600 кадров в секунду, или видеоигры, которые более щадящие при частоте 60 кадров в секунду. Я также часто использую std::chrono::high_resolution_clock для бенчмарков, хотя меня часто не волнует, стабилен ли он в этих приложениях, поскольку я могу просто запускать бенчмарки снова и снова.   -  person Christopher Mauer    schedule 23.04.2021
comment
А в этих выделенных системах как часто меняются системные часы? Вы делаете високосные секунды UTC? Вы включили dst?   -  person YSC    schedule 23.04.2021
comment
@YSC Иногда я видел реализации, в которых скаляр процессора или другие параметры управления частотой изменяются, чтобы помочь поддерживать определенные скорости передачи данных или ODR. Мы не используем тактовые частоты в ГГц, чтобы свести к минимуму ошибки, поэтому ошибки становятся значительными, и мы работаем в критически важных для безопасности приложениях. Когда мы возимся с этим, нам, конечно же, приходится обновлять наши системные часы, чтобы учитывать разницу в частоте процессора и поддерживать постоянное время кадра. Мы учитываем дополнительные секунды для приложений GNSS.   -  person Christopher Mauer    schedule 23.04.2021
comment
Тогда это может быть плохо, да. Кроме того, тип ошибки, к которой могут привести эти изменения, особенно сложно отслеживать, что не помогает.   -  person YSC    schedule 23.04.2021


Ответы (3)


Для практических целей у вас есть только 3 варианта:

  • Для работы в режиме реального времени ваш единственный выбор — std::system_clock (если вы хотите оставаться внутри С++, существуют подпрограммы уровней ОС)
  • Для измерения интервалов, если вы хотите остаться в C++, вы должны использовать std::steady_clock. Там нет реализации, которая бы имела стабильные часы с разрешением выше, чем вы получаете с std::steady_clock
  • Жизнеспособной альтернативой вышеизложенному, если вы хотите пожертвовать соответствием C++, является прямое использование счетчиков TSC. Это максимально возможное разрешение, которое можно когда-либо увидеть, а также самое быстрое в использовании. Недостатком является то, что если вы хотите измерять единицы времени, а не циклы, вам придется конвертировать циклы в секунды, используя частоту циклов ЦП.
person SergeyA    schedule 23.04.2021
comment
Как правило, если платформа не особенно благосклонна ко мне, system_clock это... ну, во встраиваемых приложениях это часто приходится определять самому. Мы часто определяем фреймы аналогично тому, что предоставляет ОС, поэтому мы часто не можем полагаться на то, что ОС предоставит нам эту информацию. Хотя теоретически я с вами согласен. На самом деле, ваш последний пункт отлично подходит для приложений RTOS. Следует ли из этого, что вы реализуете system_clock, используя счетчики циклов процессора и текущую частоту? - person Christopher Mauer; 23.04.2021
comment
@ChristopherMauer, если честно, у меня нет такого большого опыта работы со встроенными. Вы, конечно, должны иметь возможность использовать TSC для времени для системных часов, но я думаю, вопрос будет заключаться в том, как вы устанавливаете начальное время, когда чип был включен? - person SergeyA; 23.04.2021
comment
Вот пример того, как интегрировать инструкцию TSC в chrono на x86: stackoverflow.com/a/11485388/576911 - person Howard Hinnant; 23.04.2021
comment
@HowardHinnant да, на современном x86 это довольно тривиально. - person SergeyA; 23.04.2021
comment
@SergeyA К сожалению, мы обычно просто устанавливаем системное время на 0 при включении питания, когда у нас нет доступа к сети. Однако мы могли бы сохранить последнее время в NVM и использовать системное время как... общее прошедшее время выполнения. Я отвлекся. - person Christopher Mauer; 23.04.2021
comment
@HowardHinnant Спасибо. Я мало работаю с x86... обычно с ARM и микроконтроллерами. Тем не менее, я нашел вашу ссылку довольно полезной. Общий подход примерно такой же. - person Christopher Mauer; 23.04.2021
comment
@ChristopherMauer просто убедитесь, что ARM имеет монотонные TSC и синхронизирует их по нескольким процессорам. Так было не всегда с x86, но я не знаком с ARM. - person SergeyA; 23.04.2021
comment
@SergeyA Много хороших ответов, но я чувствовал, что ваши дали мне лучшие предложения для дальнейшего пути. - person Christopher Mauer; 23.04.2021

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

Дело не в том, что high_resolution_clock опасно. Вот только это довольно бесполезно. Это потому, что он всегда имеет псевдоним либо system_clock, либо steady_clock. Так что вы можете также выбрать system_clock или steady_clock, чтобы знать, какой из них вы получаете.

steady_clock всегда имеет is_steady == true. Это требование. Кроме того, system_clock никогда не имеет is_steady == true. На самом деле это не является обязательным требованием, но если на вашем компьютере нет часов, которые показывают идеальное время, время от времени их нужно будет настраивать, чтобы установить правильное время. И часы, которые можно настроить, должны иметь is_steady == false.

Ваш псевдоним frame_clock — это просто причудливый способ сказать:

using frame_clock = std::chrono::steady_clock;
person Howard Hinnant    schedule 23.04.2021
comment
Ниндзя. В любом случае, я буквально указывал OP на ваши прошлые ответы. - person Casey; 23.04.2021
comment
Если вы хотите восстановить свой пост, я проголосую за него. :-) - person Howard Hinnant; 23.04.2021
comment
Для потомков, конечно. :) - person Casey; 23.04.2021
comment
Это имеет смысл. Напрашивается вопрос, что происходит с добавлением различных часов C++20. Если, скажем, gps_clock имеет более высокое разрешение, чем steady_clock, то из этого должно следовать, что high_resolution_clock должен иметь псевдоним gps_clock. На практике в этом мало смысла, учитывая, что GPS обновляется с частотой 1 Гц. Но интересно то, что gps_clock не дает никаких гарантий, что реализация устойчива или нет. В этом случае псевдоним теоретически может быть оценен как нечто отличное от того, что вы предложили ... хотя и чрезмерно педантично. - person Christopher Mauer; 23.04.2021
comment
@ChristopherMauer меня озадачивает распространение часов в C ++. Я вижу себя покупателем только двух часов: самого высокого разрешения, отображающего реальное время, и самого высокого разрешения, стабильного и монотонного. Я не понимаю, почему мне нужно что-то большее, чем 2 из них. - person SergeyA; 23.04.2021
comment
@SergeyA Я отчасти согласен. Время GPS странное, потому что оно имеет такую ​​вещь, как дополнительные секунды. Преобразование, которое они добавили между временем UTC и временем GPS, было очень желательно для навигационных аналитиков, таких как я. Базы времени и преобразования между этими базами времени являются одной из самых сложных вещей, с которыми мы имеем дело. Однако я не могу себе представить, чтобы они были вообще полезны за пределами моей области. - person Christopher Mauer; 23.04.2021
comment
На типичном потребительском компьютере gps_clock будет реализовано с точки зрения system_clock и, следовательно, не будет устойчивым. Однако спецификация позволяет gps_clock считывать данные с GPS-приемника, поэтому, возможно, он будет стабильным. То же самое для других часов. - person Howard Hinnant; 23.04.2021
comment
@HowardHinnant Я почти ожидал, что gps_clock будет постоянно колебаться. Приемник обычно выводит время недели в миллисекундах, но, учитывая, что gps_clock основано на эпохе GPS, я ожидаю, что високосные секунды заставят часы стать нестабильными в течение этого длительного периода. Однако, если это так, то почему в стандарте не указано, что gps_clock является нестационарным? - person Christopher Mauer; 23.04.2021
comment
Время GPS физически измеряет високосные секунды, но не считает их так же, как UTC. Вместо того, чтобы отмечать 61-ю секунду в минуте, время GPS просто переходит в следующую минуту (сохраняя стабильность). Таким образом, его человеческий календарь опережает UTC на секунду во время дополнительной секунды. Например, вот текущее время UTC и GPS для сравнения: leapsecond.com/java/gpsclock.htm< /а> - person Howard Hinnant; 23.04.2021
comment
А, это имеет смысл. У меня сегодня пятничный мозг. Спасибо. Отличная дискуссия! - person Christopher Mauer; 23.04.2021

Да, вы должны быть обеспокоены. high_resolution_clock определяется реализацией. Не позволяйте реализации выбирать, просто используйте steady_clock напрямую.

Говард Хиннант написал отличное сравнение постоянных и системных часов и жалеет, что вообще никогда не добавлял high_resolution_clock.

Как и раньше, придерживайтесь использования std::chrono::steady_clock напрямую, вместо того, чтобы позволить реализации выбирать.

person Casey    schedule 23.04.2021