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

时间:2016-07-07 17:45:33

标签: c++ multithreading performance c++11 performance-testing

如已知的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周期。

  3. 同样在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?

2 个答案:

答案 0 :(得分:20)

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

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

答案很长:

draft standard实际上隐含地承认(在说明30.2.4和#34;时间规范&#34;,注释5中)时钟对象需要前进关联线程正在休眠。对于上下文,本节将解释标准库计时器对象的工作方式;计时器的行为基于用于设置它的时钟的行为。

  

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

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

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

但是,该标准似乎在20.13.7.3的定义中暗含了high_resolution_clock的两个潜在别名:

  

high_resolution_clock可能是system_clock或的同义词   steady_clock

steady_clock当然是稳定的。 system_clock ,因为系统时间可能会在程序运行时发生变化(例如,由于NTP更新)。

但是,system_clock(20.13.7.1) 仍然是&#34;实时&#34;时钟:

  

班级system_clock的对象代表来自的时钟   系统范围的实时时钟。

所以system_clock 不会在线程休眠时停止前进。 这证实了Nicol Bolas的观点,is_steady high_resolution_clock可能 false high_resolution_clock即使时钟表现如你预期的那样(即无论其状态如何,它都会前进相关线程)。

基于此,期望大多数主流实现使用high_resolution_clock某种实时(即同步)时钟似乎是合理的。毕竟,实现设计是有用的,如果它不是实时的话,时钟通常不那么有用,特别是如果按照&#34;有用的功能&#34;上方。

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

编辑:我已经就此问题启动了discussion on the ISO C++ Standards group,表明这是标准中的错误。霍华德·亨宁(Howard Hinnant)的第一个回复,值得称之为 标准,值得引用:

  

我不反对弃用steady_clock,意图在适当的弃用期后删除它。现实情况是,system_clockhigh_resolution_clock始终是一个typedef,程序员最好选择其中一个并知道他得到了什么,而不是选择high_resolution_clock并得到一些一骰子的其他时钟。

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

编辑2:

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

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

  • 使用high_resolution_clock 检查 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!"); 是否实际为实时时间别名。这可能永远不会触发,这意味着您将自动获得最高分辨率的实时&#34;时钟没有搞乱你自己的typedef:

    high_resolution_clock::is_steady
  • 如果system_clock为真,请使用HRC;否则更喜欢steady_clockhigh_resolution_clock::is_steady之间的更高分辨率时钟。 注意如果system_clock为false,则此可能仅表示HRC别名为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; 的类型相同。但是,创建自己的类型别名会使其明确,并保证即使是恶意但符合要求的实现也不会出现上述问题。

    {{1}}

答案 1 :(得分:9)

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

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

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

标准未指定high_resolution_clock::is_steady,但它确实需要实施来回答该问题。如果它是稳定的,那么你可以保证睡一个线程不会停止计时。但如果它不稳定......你根本得不到保证。