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

Ale*_*lex 17 c++ performance multithreading performance-testing c++11

如已知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;
}
Run Code Online (Sandbox Code Playgroud)

关于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 
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

结果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 
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

恢复:

  1. 当线程休眠然后clock()在g ++ 4.9.2上测量时间与其他函数不同.

  2. 当我们使用OpenMP或使用<thread>(链接)使用多线程时,然后clock()在g ++ 4.9.2上测量所有线程的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 ?

Kyl*_*and 20

简答:从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_clock或 的同义词steady_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!");
    
    Run Code Online (Sandbox Code Playgroud)
  • 使用HRC如果high_resolution_clock::is_steady是真的话; 否则更喜欢system_clock和之间的更高分辨率的时钟steady_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;
    
    Run Code Online (Sandbox Code Playgroud)

  • @KyleStrand,@ NikolBolas:这是一个`<chrono>`时钟实施调查:http://howardhinnant.github.io/clock_survey.html (3认同)

Nic*_*las 9

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

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

所以is_steady不能真正回答你的问题.

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