繁体   English   中英

标准C ++ 11是否保证high_resolution_clock能够测量实时(非CPU周期)?

[英]Does standard C++11 guarantee that high_resolution_clock measure real time (non CPU-cycles)?

由于已知的clock()可能显示小于或大于实时值 - 两种情况都显示在以下示例1和2中。

为了在C ++ 11中高精度测量时间,我们可以使用:

  • std::chrono::high_resolution_clock::now(); - 保证高精度
  • std::chrono::steady_clock::now(); - 保证实时测量
  • clock(); - 保证高精度,但测量CPU周期而不是时间
  • time(&t_start); - 不是高精度,而是实时测量

1-例如: http//ideone.com/SudWTM

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

int main(void) {

    std::cout << "sleep(3) took: \n\n";

    clock_t c_start, c_end;
    time_t t_start, t_end;
    std::chrono::high_resolution_clock::time_point h_start, h_end;
    std::chrono::steady_clock::time_point steady_start, steady_end;

    time(&t_start);  // less precise than clock() but always get the real actual time
    c_start = clock(); // clock() get only CPU-time, it can be more than real or less - sleep(3); took 0.00 seconds 
    h_start = std::chrono::high_resolution_clock::now();
    steady_start = std::chrono::steady_clock::now(); 

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

    steady_end = std::chrono::steady_clock::now();
    h_end = std::chrono::high_resolution_clock::now();
    c_end = clock();
    time(&t_end);

    std::cout << "highres = " << std::chrono::duration<double>(h_end - h_start).count() << " s \n";
    std::cout << "steady = " << std::chrono::duration<double>(steady_end - steady_start).count() << " s \n";

    printf("clock() = %.2lf seconds \n", (c_end - c_start) / (double)CLOCKS_PER_SEC);
    printf("time() = %.2lf seconds \n", difftime(t_end, t_start));

    return 0;
}

关于g ++的结果(Debian 4.9.2-10)4.9.2: clock()= 0.00秒

sleep(3) took: 

highres = 3.00098 s 
steady = 3.00098 s 
clock() = 0.00 seconds 
time() = 3.00 seconds 

C ++ MSVS 2013 v120(Windows 7x64)上的结果:

sleep(3) took:

highres = 3.00017 s
steady = 3.00017 s
clock() = 3.00 seconds
time() = 3.00 seconds

2-第二个例子OpenMP或<thread>http//coliru.stacked-crooked.com/a/2922c85385d197e1

#include <stdio.h>
#include <time.h>
#include <thread>
#include <iostream>
#include <chrono>
#include <vector>

int main(void) {

    std::cout << "for-loop took: \n\n";

    clock_t c_start, c_end;
    time_t t_start, t_end;
    std::chrono::high_resolution_clock::time_point h_start, h_end;
    std::chrono::steady_clock::time_point steady_start, steady_end;

    time(&t_start);  // less precise than clock() but always get the real actual time
    c_start = clock(); // clock() get only CPU-time, it can be more than real or less - sleep(3); took 0.00 seconds 
    h_start = std::chrono::high_resolution_clock::now();
    steady_start = std::chrono::steady_clock::now();

    #pragma omp parallel num_threads(10)
    {
        for (volatile int i = 0; i < 200000000; ++i);
    }

    steady_end = std::chrono::steady_clock::now();
    h_end = std::chrono::high_resolution_clock::now();
    c_end = clock();
    time(&t_end);

    std::cout << "highres = " << std::chrono::duration<double>(h_end - h_start).count() << " s \n";
    std::cout << "steady = " << std::chrono::duration<double>(steady_end - steady_start).count() << " s \n";

    printf("clock() = %.2lf seconds \n", (c_end - c_start) / (double)CLOCKS_PER_SEC);
    printf("time() = %.2lf seconds \n", difftime(t_end, t_start));

    int b = getchar();

    return 0;
}

结果g ++(Debian 4.9.2-10)4.9.2: clock()= 1.35秒

for-loop took: 

highres = 0.213906 s 
steady = 0.213905 s 
clock() = 1.35 seconds 
time() = 0.00 seconds 

C ++ MSVS 2013 v120(Windows 7x64)上的结果:

for-loop took:

highres = 1.49109 s
steady = 1.49109 s
clock() = 1.49 seconds
time() = 2.00 seconds

恢复:

  1. 当线程休眠时,g ++ 4.9.2上的clock()不像其他函数那样测量时间。

  2. 当我们使用OpenMP或使用<thread>link )来使用多线程时,g ++ 4.9.2上的clock()测量所有线程的CPU周期。

同样在Windows MSVS 2013上, clock()在两种情况下都会测量所需的实时时间,但这并不能保证clock()在其他平台上的测量值相同(在Linux上,g ++的睡眠为0,多线程为x倍)。

基于此,如果std::chrono::high_resolution_clock::now(); 在Windows MSVS 2013和g ++ 4.9.2两种情况下都需要实时测量,这是否能保证它能在所有其他平台上测量真正的高分辨率时间,并确保它是否能保证标准C ++ 11/14?

简短回答:从C ++ 14标准来看, high_resolution_clock没有明确提供您正在寻找的保证。

目前, steady_clocksystem_clock提供更好,更明确的保证。 但是,大多数实现可能会确保HRC在其线程处于休眠状态时前进。 尽管如此,最好还是进行自己的类型别名。 请参阅下面的“编辑”部分和评论中的讨论。

答案很长:

标准草案做其实隐含确认(附注30.2.4“时序规范”,注1) 要求时钟对象,而其相关的线程休眠推进。 对于上下文,本节将解释标准库计时器对象的工作方式; 计时器的行为基于用于设置它的时钟的行为。

[ 注意:如果时钟与稳定时钟不同步,例如CPU时钟,则这些超时可能无法提供有用的功能。 - 结束说明 ]

请注意,在这种情况下,“超时可能不提供有用的功能”意味着如果使用定时器使用非同步(非实时)时钟 sleep_until特定时钟时间,则线程将不会被唤醒 所以上面的说明有点轻描淡写。

事实上,时钟规范(20.13.3)中没有任何内容实际上需要与稳定时钟同步。

然而,标准似乎隐含纵容两个潜在的别名high_resolution_clock在20.13.7.3的定义:

high_resolution_clock可以是system_clocksteady_clock的同义词。

当然, steady_clock是稳定的。 system_clock 不是 ,因为在程序运行时系统时间可能会改变(例如,作为NTP更新的结果)。

然而, system_clock (20.13.7.1)仍然一个“实时”时钟:

system_clock类的对象表示来自系统范围实时时钟的挂钟时间。

因此,当线程休眠时, system_clock 不会停止前进。 这证实了尼科尔流星锤的观点,即一个is_steady可能是high_resolution_clock即使如你所期望的时钟的行为(即它不考虑推进其相关线程的状态)。

基于此,期望大多数主流实现对high_resolution_clock使用某种实时(即同步)时钟似乎是合理的。 毕竟,实现被设计为有用,并且如果时钟不是实时的话通常不太有用,特别是如果它按照上面“有用功能”的注释与定时器一起使用。

但是,由于无法保证 ,您应该检查要使用的每个实现的行为和/或文档。

编辑:我已经就此问题开始讨论ISO C ++标准组 ,这表明这是标准中的一个错误。 第一个回复,从霍华德Hinnant(欣南特),谁需要信贷把它标准,是值得引用:

我不反对弃用high_resolution_clock ,意图在适当的弃用期后删除它。 实际情况是,它总是一个typedef,无论是steady_clock还是system_clock ,程序员最好选择其中一个并知道他得到了什么,而不是选择high_resolution_clock并通过掷骰子获得一些其他时钟。

......因此,根据Hinnant的说法,道德不是使用high_resolution_clock

编辑2:

这个问题high_resolution_clock根据Hinnant(欣南特)是没有这么多,你可能会碰到与HRC的问题(尽管这可能的,即使使用符合标准的编译,按照上面的说法),但因为你通常不实际获得比其他两个时钟中的一个更低的分辨率(尽管你需要在类型别名或typedef中手动比较它们的分辨率以获得“最大分辨率”非睡眠时钟),没有具体的好处。 因此,您需要权衡线程在符合实现方面永远休眠的风险与名称high_resolution_clock的语义优势以及避免仅创建自己的typedef或type-alias的简单/简洁优势。

以下是各种方法的实际代码:

  • 使用static_assert 检查 high_resolution_clock是否实际是别名为真实时钟。 可能永远不会触发,这意味着您将自动获得分辨率最高的“实时”时钟而不会弄乱您自己的typedef:

      static_assert( std::is_same<high_resolution_clock, steady_clock>::value || std::is_same<high_resolution_clock, system_clock>::value, "high_resolution_clock IS NOT aliased to one of the other standard clocks!"); 
  • 如果high_resolution_clock::is_steady为true,请使用HRC; 否则更喜欢system_clocksteady_clock之间的高分辨率时钟。 请注意 ,如果high_resolution_clock::is_steady为false,这可能只意味着HRC别名为system_clock ,在这种情况下,您最终会得到一个新的类型别名,实际上与high_resolution_clock类型相同。 但是,创建自己的类型别名会使其明确,并保证即使是恶意但符合要求的实现也不会出现上述问题。

     using maxres_sys_or_steady = std::conditional< system_clock::period::den <= steady_clock::period::den, system_clock, steady_clock >::type; using maxres_nonsleeping_clock = std::conditional< high_resolution_clock::is_steady, high_resolution_clock, maxres_sys_or_steady >::type; 

该标准未从其时钟中指定此行为。 不完全是。

时钟具有is_steady静态属性,可以检查。 is_steady返回true的任何时钟都不能是那种因为你让一个线程进入休眠而停止运行的时钟。 然而,由于各种原因,该值为假的时钟可能是不稳定的。 它可能不稳定,因为它是一个挂钟,如果系统时间改变,它会改变。 或者因为滴答之间的时间段是平均值,而不是确切的数字。

所以is_steady并没有真正回答你的问题。

该标准没有指定high_resolution_clock::is_steady ,但它确实需要实现来回答该问题。 如果它是稳定的,那么你可以保证睡眠线程不会停止时钟。 但如果它不稳定......你根本得不到任何保证。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM