C++ std::chrono 在它自己的例子中给出了错误的输出

C++ std::chrono giving wrong output in its own example

所以我一直在使用时间分析器class(见下文)。 它在某些时候一直工作得很好(不工作我的意思是我怀疑它输出了奇怪的值)。然后我从头开始创建一个新的空白项目,基本上从这里复制粘贴示例:http://en.cppreference.com/w/cpp/chrono/duration/duration_cast。相反,当它显然应该是 1000 时,它现在打印 1014,就像它直到昨天才做的那样! 再一次,上面 link 中的相同示例曾经工作直到昨天。我不知道发生了什么事。我重新启动了我的机器,但它仍然没有工作。

这是时间分析器 class:

#pragma once

#include <stdio.h>
#include <time.h>
#include <chrono> // C++11
#include <thread>
#include <string>

namespace profiler
{
// The time profiling class
class Time
{
public:
    Time(const std::string& str) 
        : m_str(str), m_start(std::chrono::system_clock::now()) { }

    virtual ~Time()
    {
        auto end = std::chrono::system_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - m_start).count();

        printf("%s took %lli milliseconds\n", m_str.empty() ? "Block" : m_str.c_str(), duration);
    }

private:
    std::string m_str;
    std::chrono::system_clock::time_point m_start;
};
}

#ifdef _DEBUG
// Profile only if debugging. This profiles the time spent to process the block that this macro was called within
#ifndef TIME
#define TIME(str) profiler::Time timer__(str)
#endif // TIME
#else
// If not debugging, do nothing
#ifndef TIME
#define TIME(str) do { } while(0) // This avoids empty statements
#endif // TIME
#endif // _DEBUG

#ifndef SLEEP
#define SLEEP(ms) std::this_thread::sleep_for(std::chrono::milliseconds(ms));
#endif

// A working example of this profiler. Call EXAMPLE() and it should print 16 milliseconds
#ifndef EXAMPLE
#define EXAMPLE() \
    profiler::Time timer__("Example that takes 16 milliseconds (value should match)"); \
    std::this_thread::sleep_for(std::chrono::milliseconds(1)); \
    std::this_thread::sleep_for(std::chrono::milliseconds(2)); \
    std::this_thread::sleep_for(std::chrono::milliseconds(3)); \
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
#endif

使用代码如下:

#include <stdio.h>
#include <chrono>
#include <thread>

int main()
{
    auto start = std::chrono::system_clock::now();

    std::this_thread::sleep_for(std::chrono::seconds(1));

    auto end = std::chrono::system_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();

    printf("Block took %lli milliseconds\n", duration); 

    return getchar();
}

我在 Windows 7 Professional 64 位上使用 Visual Studio Ultimate 2012,如果有帮助的话。

std::this_thread::sleep_for(std::chrono::seconds(1)); 无法保证 正好 1 秒。

用了1.014秒,包括第二次调用now(),应该算是够用了。

很可能是另一个调用 timeBeginPeriod 的应用程序干扰了您的测量。这是一个例子:

这是一个测量 1000 毫秒睡眠的应用程序,然后生成另一个调用 timeBeingPeriod(1) 和 timeEndPeriod(1) 的应用程序。请注意调用 timeBeginPeriod 的第二个应用程序如何影响此应用程序的时间测量:

#include <Windows.h>
#include <chrono>
#include <iostream>

int main(int nArgs, char**args)
{
    if (nArgs <= 1)
    {
        // if we're spawned normally measure sleeping for 1000ms 30 times
        for (int i = 0; i < 30; ++i)
        {
            auto timeBegin = std::chrono::system_clock::now();
            Sleep(1000);
            auto timeEnd = std::chrono::system_clock::now();
            auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(timeEnd - timeBegin);
            std::cout << "Iteration " << i << ", sleeping for 1000ms took " << duration.count() << "ms..." << std::endl;
            // On the 10th iteration spawn a bad app which calls timeBeginPeriod(1)
            if (i == 10)
            {
                std::cout << "Spawning bad process" << std::endl;
                PROCESS_INFORMATION pi = {};
                STARTUPINFOA si = { sizeof(STARTUPINFOA) };
                CreateProcessA("..\Debug\Timer.exe", "be a bad process", nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi);
            }
        }
    }
    else
    {
        // If we're spawned with some arguments pretend to be a bad app that calls timeBeginPeriod(1)
        std::cout << "Bad process calling timeBeginPeriod(1)" << std::endl;
        timeBeginPeriod(1);
        Sleep(10 * 1000);
        std::cout << "Bad process calling timeEndPeriod(1)" << std::endl;
        timeEndPeriod(1);
    }

}

给出:

Iteration 0, sleeping for 1000ms took 1015ms...
Iteration 1, sleeping for 1000ms took 1015ms...
Iteration 2, sleeping for 1000ms took 1015ms...
Iteration 3, sleeping for 1000ms took 1015ms...
Iteration 4, sleeping for 1000ms took 1015ms...
Iteration 5, sleeping for 1000ms took 1015ms...
Iteration 6, sleeping for 1000ms took 1015ms...
Iteration 7, sleeping for 1000ms took 1015ms...
Iteration 8, sleeping for 1000ms took 1015ms...
Iteration 9, sleeping for 1000ms took 1015ms...
Iteration 10, sleeping for 1000ms took 1015ms...
Spawning bad process
Bad process calling timeBeginPeriod(1)
Iteration 11, sleeping for 1000ms took 1011ms...
Iteration 12, sleeping for 1000ms took 1001ms...
Iteration 13, sleeping for 1000ms took 1001ms...
Iteration 14, sleeping for 1000ms took 1001ms...
Iteration 15, sleeping for 1000ms took 1000ms...
Iteration 16, sleeping for 1000ms took 1000ms...
Iteration 17, sleeping for 1000ms took 1001ms...
Iteration 18, sleeping for 1000ms took 1001ms...
Iteration 19, sleeping for 1000ms took 1001ms...
Bad process calling timeEndPeriod(1)
Iteration 20, sleeping for 1000ms took 1008ms...
Iteration 21, sleeping for 1000ms took 1011ms...
Iteration 22, sleeping for 1000ms took 1015ms...
Iteration 23, sleeping for 1000ms took 1015ms...
Iteration 24, sleeping for 1000ms took 1016ms...
Iteration 25, sleeping for 1000ms took 1015ms...
Iteration 26, sleeping for 1000ms took 1015ms...
Iteration 27, sleeping for 1000ms took 1015ms...
Iteration 28, sleeping for 1000ms took 1015ms...
Iteration 29, sleeping for 1000ms took 1015ms...

请注意,在一般情况下,我们测量的时间太长了 15 毫秒,但是 'bad' 应用 运行ning 时我们的准确度要高得多。

您应该使用更准确的时钟来计时。 QueryPerformanceCounter/QueryPerformanceFrequency GetSystemTimeAsFileTimePrecise std::chrono::high_resolution_clock 仅在 VS2015 上有效。 std::chrono::high_resolution_clock 在 vs2013 上有点垃圾,仍然有这个问题。

但是,这只能解释您所看到的情况,在一般情况下,sleep(xxx) 会休眠 xxx 和一些额外的时间 - 它只会在有备用 CPU 内核时再次启动 运行 它在 它的下一个计划。请不要自己使用 timeBeginPeriod,因为它不好,只需编写您的逻辑来处理您没有 运行 实时系统的事实,因此任何测量都会有一些错误。