У меня есть пример кода ниже, чтобы измерить время выполнения некоторого фрагмента кода:
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.
«Это специфический вопрос для Windows». Пробовали ли вы использовать QueryPerformanceCounter?
Я смог воспроизвести его стабильно. Дождался 30-секундного сна, вручную перевел машину в спящий режим, и когда я снова включил ее, она продолжила выполняться и напечатала время, превышающее 30, в зависимости от того, как долго я ждал.
@MooseBoys К сожалению, проблема воспроизводится и с этой функцией.
@finalreq Это указывает на то, что система не приостановлена, а находится в частичном спящем состоянии. Согласно MSDN, более новые версии msvc реализуют как steady_clock
, так и high_resolution_clock
с использованием QPC. std::chrono
, к сожалению, был написан без особого внимания к множеству различных состояний сна, поддерживаемых современными компьютерами, поэтому они не очень полезны для измерения времени между изменениями состояния питания. Если ваша цель — измерить общее время работы ядра, вам придется использовать ETW.
А как насчет GetTickCount
? Поскольку это системный тик, возможно, он не увеличивается, когда система спит. Если вы хотите сохранить часы с высоким разрешением, вы можете использовать оба и использовать только GetTickCount
для корректировки ваших точных часов.
@ElderBug также воспроизводится с помощью GetTickCount.
Я думаю, что, возможно, нашел ответ. QueryUnbiasedInterruptTimePrecise()
судя по документации, кажется правильным. Буду тестировать и обновлять пост, если он работает
Вы правы, проще всего использовать счетчик, который увеличивается каждую секунду. Это легко реализовать с помощью потоков:
#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;
}
«Я понял, что если компьютер перейдет в спящий режим между оператором auto before = ... и оператором auto after = ...». Вы только что подумали об этом или действительно видели, как это происходит? Я не знаю точно, как ведет себя
steady_clock
во сне, но я бы не стал просто предполагать, что это так.