【问题标题】:Measuring time results in return values of 0 or 0.001测量时间导致返回值为 0 或 0.001
【发布时间】:2013-03-22 23:47:59
【问题描述】:

我正在尝试使用chrono::steady_clock 来测量程序中一段代码之间经过的小数秒数。我在 LiveWorkSpace (http://liveworkspace.org/code/YT1I$9) 中有这段代码:

#include <chrono>
#include <iostream>
#include <vector>

int main()
{
    auto start = std::chrono::steady_clock::now();
    for (unsigned long long int i = 0; i < 10000; ++i) {
       std::vector<int> v(i, 1);
    }
    auto end = std::chrono::steady_clock::now();

    auto difference = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();

    std::cout << "seconds since start: " << ((double)difference / 1000000);
}

当我像这样在我的程序中实现相同的想法时:

auto start = std::chrono::steady_clock::now();
// block of code to time
auto end = std::chrono::stead_clock::now();

auto difference = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()

std::cout << "seconds since start: " << ((double) difference / 1000000);

程序只会打印出00.001 的值。我非常怀疑我的代码块的执行时间总是等于 01000 微秒,那么这个舍入的原因是什么,我该如何消除它以便获得正确的小数值?

这是一个 Windows 程序。

【问题讨论】:

  • 改用std::chrono::high_resolution_clock
  • @KerrekSB:这是一个很好的建议,但steady_clock::now() 应该已经精确到纳秒,或者至少滴答声(100 纳秒间隔)。我怀疑这里有一个数学问题。
  • @RobertHarvey:哦,可能是演员表中放错位置的括号?
  • @KerrekSB 哪个括号放错了?在我看来是正确的。
  • @KerrekSB 对于它的价值,无论我使用high_resolution_clocksteady_clock 还是monotonic_clock,返回值都是相同的。它们总是00.001

标签: c++ windows time c++11 chrono


【解决方案1】:

在 MSVC2012 上运行一些测试后,我可以确认 Microsoft 实现中的 C++11 时钟没有足够高的分辨率。有关此问题的错误报告,请参阅 C++ header's high_resolution_clock does not have high resolution

因此,不幸的是,对于更高分辨率的计时器,您需要像这样直接使用 boost::chrono 或 QueryPerformanceCounter,直到他们修复错误:

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

int main()
{
    LARGE_INTEGER frequency;
    QueryPerformanceFrequency(&frequency);

    LARGE_INTEGER start;
    QueryPerformanceCounter(&start);

    // Put code here to time

    LARGE_INTEGER end;
    QueryPerformanceCounter(&end);

    // for microseconds use 1000000.0
    double interval = static_cast<double>(end.QuadPart- start.QuadPart) / 
                      frequency.QuadPart; // in seconds
    std::cout << interval;
}   

【讨论】:

  • 啊,难怪它不能正常工作。我想更多地了解QueryPerformanceFrequency() 函数及其实际作用。我应该总是在启动计时器之前调用该函数,还是在程序期间只需要调用一次的函数?我们是否正确划分了频率?结果现在给了我数百秒,这似乎也不正确。
  • @raphnguyen: QueryPerformanceFrequency 只应在程序期间调用一次以获取频率。我展示的示例在microseconds 中,如果您想要秒删除1000000.0。我将代码更改为秒。
  • @raphnguyen:QueryPerformanceFrequency 返回以每秒计数为单位的频率,因此如果将计数除以该频率,则得到秒数。
  • 它现在似乎只返回0。这些查询函数的精度是多少?我不知道应该预计什么时间。给出的示例显示我正在实现的查找功能的时间为 0.002 秒。这是对从磁盘读取文件的密钥的二进制搜索,所以我想至少需要 0.001 秒来查找文件。
  • 天啊,他们将其解决为“延期”!耶!耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶耶!!!这是我在过去几年微软发送给我的每一项调查中要求的唯一一件事,即他们将停止(ab)使用“WONTFIX”关闭原因,因为他们同意应该修复的事情,但对于版本来说已经太晚了准备发布。
【解决方案2】:

这个问题已经有了很好的答案。但我想补充一点:

&lt;chrono&gt; 框架内工作。建立自己的时钟。建立自己的时间点。建立自己的持续时间。 &lt;chrono&gt; 框架是非常可定制的。通过在该系统中工作,您不仅会学习std::chrono,而且当您的供应商开始运送您满意的时钟时,将您的代码从您的手动chrono::clock 转换为std::high_resolution_clock 将是微不足道的(或其他)。

首先,对您的原始代码提出一点批评:

std::cout << "seconds since start: " << ((double) difference / 1000000);

每当您看到自己引入转换常量(如 1000000)来获得您想要的东西时,您就没有正确使用 chrono。您的代码不正确,只是脆弱。你确定你在那个常数中得到了正确数量的零吗?!

即使在这个简单的例子中,你也应该对自己说:

我想以双精度表示的秒数来查看输出。

然后您应该使用chrono 为您执行此操作。一旦你学会了如何做就很容易了:

typedef std::chrono::duration<double> sec;
sec difference = end - start;
std::cout << "seconds since start: " << difference.count() << '\n';

第一行创建了一个周期为 1 秒的类型,用双精度表示。

第二行只是减去您的 time_points 并将其分配给您的自定义持续时间类型。从steady_clock::time_point 的单位到您的自定义持续时间(双秒)的转换由chrono 库自动完成。这比:

auto difference = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()

最后你只需使用.count() 成员函数打印出你的结果。这又比以下简单得多:

std::cout << "seconds since start: " << ((double) difference / 1000000);


但是由于您对std::chrono::steady_clock 的精度不满意,并且您可以访问 QueryPerformanceCounter,因此您可以做得更好。您可以在 QueryPerformanceCounter 之上构建自己的时钟。

&lt;disclaimer&gt;

我没有 Windows 系统来测试以下代码。

&lt;/disclaimer&gt;

struct my_clock
{
    typedef double                             rep;
    typedef std::ratio<1>                      period;
    typedef std::chrono::duration<rep, period> duration;
    typedef std::chrono::time_point<my_clock>  time_point;
    static const bool is_steady =              false;

    static time_point now()
    {
        static const long long frequency = init_frequency();
        long long t;
        QueryPerformanceCounter(&t);
        return time_point(duration(static_cast<rep>(t)/frequency));
    }
private:
    static long long init_frequency()
    {
        long long f;
        QueryPerformanceFrequency(&f);
        return f;
    }
};

由于您希望输出以双秒为单位,因此我将此时钟的 rep 设置为 doubleperiod 1 秒。您可以轻松地将rep 积分和period 其他一些单位,例如微秒或纳秒。您只需在now() 中调整typedefs 和从QueryPerformanceCounter 到您的duration 的转换。

现在您的代码看起来很像您的原始代码:

int main()
{
    auto start = my_clock::now();
    for (unsigned long long int i = 0; i < 10000; ++i) {
       std::vector<int> v(i, 1);
    }
    auto end = my_clock::now();

    auto difference = end - start;
    std::cout << "seconds since start: " << difference.count() << '\n';
}

但是没有手动编码的转换常数,并且(我希望是)足够的精度来满足您的需求。并且更容易移植路径到未来的std::chrono::steady_clock 实现。

&lt;chrono&gt; 被设计成一个可扩展的库。请延长它。 :-)

【讨论】:

  • 根据我在 VS 2017 15.6.7 上的经验,QueryPerformanceCounter() 和 QueryPerformanceFrequency 的参数需要是 LARGE_INTEGER microsoft 类型。
  • is_steady = false 有什么原因吗?我会假设 QueryPerformanceCounter 是一个 stable_clock。
  • @TysonHilmer:我敢肯定,这两个都是不错的 cmets。我不是 Windows buf,所以这段代码未经测试。只是希望展示如何将本机操作系统时序集成到&lt;chrono&gt; 库中的大致方向。
猜你喜欢
  • 2013-05-19
  • 2019-05-26
  • 2020-08-15
  • 1970-01-01
  • 1970-01-01
  • 2013-08-15
  • 2017-08-12
  • 2014-11-21
  • 2012-08-13
相关资源
最近更新 更多