【问题标题】:C++ std::chrono giving wrong output in its own exampleC++ std::chrono 在其自己的示例中给出错误的输出
【发布时间】:2015-10-20 17:29:05
【问题描述】:

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

这是时间分析器类:

#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。

【问题讨论】:

  • 它按预期运行。这里有两件事要考虑。首先,sleep_for 将休眠 at least 指定的休眠时间。还有 sleep 方法前后消耗的时间。
  • @wendelbsilva 我知道这个睡眠功能可能不是非常准确,而且 SO 可能会干扰一点,但我坚信有些地方出了问题。我一直在用它来测量解析某些文件所花费的时间。平均过去是 10 到 15 毫秒,但现在有时会打印零!现在这个解析不可能花费 0 毫秒。太奇怪了,它甚至会像以前一样打印 15 毫秒,有时甚至是 0。
  • 您计算机上的另一个应用程序是否调用 timeBeginPeriod temporarilt 提高了您的时间分辨率?游戏对此特别有罪(注意更改计时器周期通常是一个非常糟糕的主意)
  • @Mike 我不明白

标签: c++ chrono


【解决方案1】:

std::this_thread::sleep_for(std::chrono::seconds(1)); 无法保证睡眠准确 1 秒。

使用 1.014 秒,包括对now() 的第二次调用,必须被认为足够好。

【讨论】:

  • 我一直在用它来衡量解析某个文件所花费的时间。平均过去是 10 到 15 毫秒,但现在有时打印为零!这种解析不可能花费 0 毫秒。根据我昨天进行的压力测试,我可以向您保证,对 now() 的调用不需要 14 毫秒。但是,这可能与 SO 有关。真正令人好奇的是,当我回复您的 cmets 时,我再次运行了该应用程序几次,它现在打印出正确的值,即使是非常小的睡眠,例如 3 毫秒
  • 这是一个完全不同的问题。如果基准测试花费 0 时间,通常意味着优化器删除了所有代码。综合基准​​很难!
  • 查看我的评论。 Windows 上 system_clock 的精度约为 15 毫秒,因此如果速度足够快,您将看到 0 或 15
【解决方案2】:

很可能是另一个应用程序调用 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 毫秒的时间太长了,但当“坏”应用程序运行时,我们会更加准确。

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

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

【讨论】:

  • 很难分辨出哪些应用程序调用了 timePeriods 函数,对吧?是否有选项可以包含 timePeriods 函数而不会遇到可移植性问题来改进此分析类?顺便说一句,high_resolution_clock 只是一个 system_clock typedef。
  • @Yves Henri 如果这是用于分析类,请首先考虑,您应该使用推荐的高质量计时功能。 chrono 中的时钟都不是(至少在 VS2012 上)。其次,睡眠因不准确而臭名昭著-如果您实际上是在测量一些真实的代码,那么您的计时会更准确。但最重要的是,您不是在实时操作系统上运行,因此您的线程将被抢占,从而导致可重复性成为问题。这就是为什么你应该使用与调度程序合作的分析器,因为这可能是准确的,或者只是忍受你得到的不准确
  • 如果你想知道哪些计时器在 Windows 上是好的,msdn.microsoft.com/en-gb/library/windows/desktop/… 是必读的。
  • 我只是认为chrono(VS2012的'C++11'编译器附带的那个)是我可以使用的最精确的时钟,所以我找到了这个类并做了一些调整。它运行平稳,但在某些时候它开始为过去 10~15ms 的相同任务输出 0ms(这不是睡眠调用。为了完整起见,它是一个文件解析函数)。你知道我可以用什么免费的精确时间分析器在我的 Windows 上进行测试吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-21
  • 2021-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多