Счетчик времени Chrono неточен, когда машина переходит в спящий режим в середине выполнения?

У меня есть пример кода ниже, чтобы измерить время выполнения некоторого фрагмента кода:

int main()
{   
    auto before = chrono::steady_clock::now();

    Sleep(30000);

    auto after = chrono::steady_clock::now();

    int duration = (std::chrono::duration_cast<std::chrono::seconds> ((after - before)).count());
    cout << duration << endl;

    return 0;
}

Обычно он работает нормально и выводит 30 в выражении cout.

Однако во время тестирования я заметил, что если компьютер переходит в спящий режим между оператором auto before = ... и оператором auto after = ... (из-за бездействия или по какой-либо другой причине), то распечатанное время также учитывает все время, в течение которого машина находилась в спящем режиме. Это имеет смысл, поскольку мы сравниваем момент времени до того, как машина заснет, и момент времени после.

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

Это конкретный вопрос Windows. Насколько я понимаю, в MacOS есть mach_absolute_time именно то, что я ищу в windows. В качестве компилятора я использую MSVC 19.29.30147.0.

«Я понял, что если компьютер перейдет в спящий режим между оператором auto before = ... и оператором auto after = ...». Вы только что подумали об этом или действительно видели, как это происходит? Я не знаю точно, как ведет себя steady_clock во сне, но я бы не стал просто предполагать, что это так.

Nicol Bolas 09.02.2023 20:37

«Это специфический вопрос для Windows». Пробовали ли вы использовать QueryPerformanceCounter?

MooseBoys 09.02.2023 20:39

Я смог воспроизвести его стабильно. Дождался 30-секундного сна, вручную перевел машину в спящий режим, и когда я снова включил ее, она продолжила выполняться и напечатала время, превышающее 30, в зависимости от того, как долго я ждал.

finalreq 09.02.2023 20:40

@MooseBoys К сожалению, проблема воспроизводится и с этой функцией.

finalreq 09.02.2023 22:22

@finalreq Это указывает на то, что система не приостановлена, а находится в частичном спящем состоянии. Согласно MSDN, более новые версии msvc реализуют как steady_clock, так и high_resolution_clock с использованием QPC. std::chrono, к сожалению, был написан без особого внимания к множеству различных состояний сна, поддерживаемых современными компьютерами, поэтому они не очень полезны для измерения времени между изменениями состояния питания. Если ваша цель — измерить общее время работы ядра, вам придется использовать ETW.

MooseBoys 09.02.2023 22:52

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

ElderBug 09.02.2023 22:58

@ElderBug также воспроизводится с помощью GetTickCount.

finalreq 09.02.2023 23:18

Я думаю, что, возможно, нашел ответ. QueryUnbiasedInterruptTimePrecise() судя по документации, кажется правильным. Буду тестировать и обновлять пост, если он работает

finalreq 09.02.2023 23:29
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
8
93
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

#include <thread>
#include <atomic>
#include <chrono>

using namespace std::literals::chrono_literals;

class ellapsed_counter {
    std::atomic<bool> finished = false;
    std::atomic<unsigned int> value = 0;
    
    std::thread worker { [this] {
        while(!finished) {
            value++;
            std::this_thread::sleep_for(1s);
        }
    } };

public:
    void finish() noexcept { 
        finished = true; 
        if (worker.joinable()) worker.join();
    }
    unsigned int ellapsed() const noexcept { return value; }
};

Это будет увеличиваться с интервалом в 1 с (возможно, с некоторой ошибкой), пока процесс работает, и должен прекратиться, когда он спит. Вы можете использовать его следующим образом:

#include <iostream>

int main(int argc, const char *argv[]) {
    ellapsed_counter counter;
    unsigned int last = 0, count = 0;

    while(count < 10) {
        count = counter.ellapsed();
        if (count != last) {
            last = count;
            std::cout << count << std::endl;
        }
    }
    counter.finish();

    return 0;
}

Это будет считать от 1 до 10 секунд и выйти.

Ответ принят как подходящий

После осмотра и тестирования решение состоит в том, чтобы использовать QueryUnbiasedInterruptTime

Запустив следующий фрагмент кода, я вручную перевел свою машину в спящий режим, в то время как программа застряла на операторе сна, и я заметил, что вторая распечатка постоянно выводит 15 секунд независимо от того, как долго я оставляю свою машину в спящем состоянии. Однако первая распечатка с использованием GetTickCount64 будет включать количество времени, в течение которого машина находилась в спящем режиме.

int main()
{

    ULONGLONG before_query, after_query= 0;
    QueryUnbiasedInterruptTime(&before_query);
    auto beforeticks = GetTickCount64();
   
    Sleep(15000);

    QueryUnbiasedInterruptTime(&after_query);
  
    auto afterticks = GetTickCount64();


    cout << "Ticks from gettickcount64 is " << (double (afterticks-beforeticks))/1000 << endl;

    cout << "Unbiased time measure is " << double((after_query - before_query)/10000000) << endl;

    return 0;
}

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