Chrono时间计数器在执行过程中计算机休眠时不准确?

huangapple go评论79阅读模式
英文:

Chrono time counter is inaccurate when machine goes to sleep in the middle of execution?

问题

下面是代码示例,用于测量某段代码的执行时间:

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;
}

通常情况下,它可以正常工作并在cout语句中打印出30。

然而,在测试过程中,我观察到,如果计算机在auto before = ...语句和auto after = ...语句之间进入睡眠状态(因为长时间不活动或其他原因),那么打印出的时间也会包括计算机进入睡眠状态的整个时间。这是合理的,因为我们正在比较计算机进入睡眠状态之前和之后的时间点。

因此,我的问题是如何使计算机进入睡眠状态的时间不计入最终的持续时间?可能需要一个不会在计算机进入睡眠状态时递增的计时器,而不是时间点测量,但我不知道有这样的计时器。

这是一个特定于Windows的问题。据我了解,MacOS有mach_absolute_time,这正是我在Windows中寻找的东西。我正在使用MSVC 19.29.30147.0作为我的编译器。

英文:

I have the code sample bellow to measure the execution time of some piece of code:

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

    Sleep(30000);

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

    int duration = (std::chrono::duration_cast&lt;std::chrono::seconds&gt; ((after - before)).count());
    cout &lt;&lt; duration &lt;&lt; endl;

    return 0;
}

Normally it works fine and prints out 30 in the cout statement.

However, during testing I observed that if the computer were to go to sleep in between the auto before = ... statement and the auto after = ... statement (due to inactivity or whatever other reason), then the printed out time also counts the entire time the machine was asleep. This makes perfect sense since we are comparing a timepoint from before the machine going to sleep and one with after.

So my question is how can I make it so that the duration the machine was asleep is not counted in my final duration? Probably will need a ticker that doesn't increment while machine is asleep rather than timepoint measurements but I'm not aware of such a ticker.

This is a Windows specific question. As I understand, MacOS has mach_absolute_time which is exactly what I'm looking for in windows. I'm using MSVC 19.29.30147.0 as my compiler.

答案1

得分: 3

After looking around and testing it out, the solution is to use QueryUnbiasedInterruptTime.

获取当前的无偏中断时间计数,单位为100纳秒。无偏中断时间计数不包括系统处于休眠或休眠状态时的时间。

Running the following code snippet, I manually put my machine to sleep while the program was stuck on the sleep statement and I observed that the second print out consistently outputs 15 seconds regardless of how long I leave my machine in a sleeping state. However, the first print-out that uses GetTickCount64 will include the amount of time the machine was asleep.

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;
}
英文:

After looking around and testing it out, the solution is to use QueryUnbiasedInterruptTime

> Gets the current unbiased interrupt-time count, in units of 100 nanoseconds. The unbiased interrupt-time count does not include time the system spends in sleep or hibernation.

Running the following code snippet, I manually put my machine to sleep while the program was stuck on the sleep statement and I observed that the second print out consistently outputs 15 seconds regardless of how long I leave my machine in a sleeping state. However, the first print-out that uses GetTickCount64 will include the amount of time the machine was asleep.

int main()
{

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

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


    cout &lt;&lt; &quot;Ticks from gettickcount64 is &quot; &lt;&lt; (double (afterticks-beforeticks))/1000 &lt;&lt; endl;

    cout &lt;&lt; &quot;Unbiased time measure is &quot; &lt;&lt; double((after_query - before_query)/10000000) &lt;&lt; endl;

    return 0;
}

答案2

得分: 0

以下是代码的翻译部分:

#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; }
};
#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;
}

这将在每秒递增(可能存在一些误差),只要进程在运行,它将继续递增,而在休眠时会停止。

英文:

You are correct that the easiest way is to use a counter that is incremented each second. This is easily implemented with threads:

#include &lt;thread&gt;
#include &lt;atomic&gt;
#include &lt;chrono&gt;

using namespace std::literals::chrono_literals;

class ellapsed_counter {
    std::atomic&lt;bool&gt; finished = false;
    std::atomic&lt;unsigned int&gt; 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; }
};

This will keep incrementing on 1s intervals (probably with some error) as long as the process is running and should cease so when it is sleeping.
You can use it like this:

#include &lt;iostream&gt;

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

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

    return 0;
}

This will count from 1 to 10 seconds and exit.

huangapple
  • 本文由 发表于 2023年2月10日 03:10:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/75403351.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定